the equivalent of OO class data and methods

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

the equivalent of OO class data and methods

Michael Mossey
I'm making the transition from OO to Haskell, and a major problem  for me is that I'm
used to OO, where member variables are distinct from other variables and functions,
so I can use totally generic member variable names and never have a conflict with
other member variables or local variables.

For example, if I want to create LayoutItem and name its coordinates x and y, I do this:

(Python)
class LayoutItem :
   def __init__( self, x, y ) :
     self.x, self.y = (x, y )

Then later,
p = LayoutItem( 10, 20 )

Then later I can refer to p.x and p.y.

I can also create other classes and name *their* member variables x and y, with no
confusion.

Now, I know that in Haskell I can do

data LayoutItem = LayoutItem { x, y :: Int }

Later,

let p = LayoutItem { x = 10, y = 20 }

I have read that this automatically creates "accessor" functions called x and y, so I
can refer to (x p) and (y p)

let foo = (x p)^2 + (y p)^2

Now let's say I create another type that also uses x and y:

data Notehead  = Notehead { x, y :: Int }

n = Notehead { x = 100, y = 200 }

This doesn't work. Apparently the accessor functions x and y (for LayoutItem) have
global scope and conflict with those for Notehead.

Which means I have to name them different things, which is awkward. For example,

data LayoutItem = LayoutItem { layoutItemX, layoutItemY :: Int }
data Notehead = Notehead { noteheadItemX, noteheadItemY :: Int }

Also, I tried using some of these accessor function names as variables, and the
compiler doesn't distinguish between them. For example,

myfunc :: Notehead -> LayoutItem -> Int
myfunc n p = let layoutItemY = layoutItemX p
                  z = layoutItemY n
              in z^2

This doesn't work, because declaring layoutItemY as a variable hides its defintion as
an accessor function.

Is there a way to get what I want from Haskell?

Thanks,
Mike




Reply | Threaded
Open this post in threaded view
|

the equivalent of OO class data and methods

Yitzchak Gale
Michael Mossey wrote:
> For example, if I want to create LayoutItem and name its coordinates x and
> y...
> Then later I can refer to p.x and p.y.
> I can also create other classes and name *their* member variables x and y,
> with no confusion.

There are two ways to share names in Haskell.

One way, which is most similar to the OO approach, is to use
encapsulation. Haskell uses modules for encapsulation
and namespace control. However, GHC currently requires
each module to reside in a separate source file (even though
that is not required by the standard).

When you use this method, you need to use dotted names
to qualify them, as in your OO example. If only one version
of a name is used in a given module, you can choose to
import that name unqualified into the namespace of that
module.

The other approach is using the class system:

class Coordinates a where
  x :: a -> Int
  y :: a -> Int

data LayoutItem = LayoutItem { layoutItemX, layoutItemY :: Int }

instance Coordinates LayoutItem where
  x = layoutItemX
  y = layoutItemY

data Notehead = Notehead { noteheadItemX, noteheadItemY :: Int }

instance Coordinates Notehead where
  x = NoteheadX
  y = NoteheadY

Note that using this approach, the types of x and y always need
to have exactly the same shape: a -> Int, where a is the type for
which you are declaring the class instance. You can't use Int
for one type and Float for another, for example.

You can get around that restriction using Haskell extensions such
as type families, or multiparameter classes and functional
dependencies.

With the approach, you do not qualify the names. You just re-use
the names, and the type checker figures out what you mean from
the context. In case of ambiguity, you use explicit type signatures
to clarify the situation.

Hope this helps,
Yitz
Reply | Threaded
Open this post in threaded view
|

the equivalent of OO class data and methods

Michael Mossey
Thanks. One question:

Yitzchak Gale wrote:
>
> One way, which is most similar to the OO approach, is to use
> encapsulation.

> The other approach is using the class system:
>
> class Coordinates a where
>   x :: a -> Int
>   y :: a -> Int
>
> data LayoutItem = LayoutItem { layoutItemX, layoutItemY :: Int }
>
> instance Coordinates LayoutItem where
>   x = layoutItemX
>   y = layoutItemY
>
> data Notehead = Notehead { noteheadItemX, noteheadItemY :: Int }
>
> instance Coordinates Notehead where
>   x = NoteheadX
>   y = NoteheadY
>

In this case, do local variables still shadow or hide the functions
contained by the class? In other words, would this work?

foo :: LayoutItem -> Notehead -> Int
foo l n = let y = x l + x n
               z = y l + y n
           in z + y

I'm guessing that the variable y will shadow the function y. This is a
problem, because I want generic names available for variables.

Now, using encapsulation in modules, I guess it would look like this:

import qualified LayoutItem (x, y)
import LayoutItem ( LayoutItem )
import qualified Notehead (x, y)
import Notehead ( Notehead )

foo :: LayoutItem -> Notehead -> Int

foo l n = let y = LayoutItem.x l + Notehead.x n
               z = LayoutItem.y l + Notehead.y n
           in z + y