
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

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

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
participants (3)
-
Michael Mossey
-
Michael P Mossey
-
Yitzchak Gale