tags:

views:

200

answers:

3

Coming to Haskell from a background in various OO languages, one thing that seems like a bit of a drawback to me is that function and field names aren't scoped to the types they're associated with, so it's easy to run into clashes if different datatypes have fields with the same name.

If I have these three modules:

module One where

data Foo a = Foo { value :: a }

----

module Two where

data Bar a = Bar { value :: a }

----

module Three where

import One
import Two

foo = Foo { value = 42 }  -- compile error here
n = value foo  -- and here

the unqualified references to value in module Three are considered ambiguous even though only one of the two imported names makes sense in this context. (In an OO language, references to foo.value and bar.value would be unambiguous.)

Of course I can disambiguate by writing Foo { One.value = 42 }, but that looks awkward. I can also name the fields differently, e.g. "fooValue" and "barValue", but the redundancy in Foo { fooValue = 42 } looks awkward as well.

This is really a special case of the more general issue of functions in different modules that have the same name but operate on different, unrelated types. I seem to run into it more often with field names, though. For example, I have several datatypes, not related by a type class but often used together, that contain color values, so I'd like each one to have a field named "color".

How do experienced Haskell developers name things, and organize them into modules, to avoid this type of situation?

+1  A: 

You might consider a type class, if there's a common accessor function on all these types. E.g.

class Fieldable a where
     field :: a -> b

instance Fieldable (a,b) where
     field = fst

Etc.

Don Stewart
Can a datatype's field names be part of a class instance? If I make a `class Valued a b where value :: a -> b` then to associate my two types with it, I'd have to do something like `instance Valued (Foo a) a where value = value` and that's not valid (the two meanings of `value` conflict). It seems I can't define a function as part of a class instance if its name is already defined beforehand.
Wyzard
+2  A: 

How do experienced Haskell developers name things, and organize them into modules, to avoid this type of situation?

I've only worked with a few experienced Haskell developers, and they do awful stuff like

data Foo a = Foo { foo_value :: a }

data Bar a = Bar { bar_value :: a }

or even

data Apocalypse a = A { ap_value :: a }

In general, I have the feeling that a lot of old-time Haskellers don't like qualified names and really want to pretend that the world has just one big name space, straight out of the dark ages. (There was a time that C compilers had the same restrictions on field names, which is why the mode in a struct stat is called st_mode and not just plain mode.)

You can overload names with type classes, but the experienced developers I know don't like gratuitous type classes. I can never figure out when they think a type class will be gratuitous or not.

I hope that one day Haskell people will come to terms with a hierarchical name space and will start using qualified names. As God intended.

Norman Ramsey
When you don't actually want polymorphic record selectors, then it makes sense to me to not force them just for some minor syntactic convenience. Even if you can use things qualified, it seems to me that claiming a common name for a rarely used field, especially one which should usually only be accessed through a helper function, is bad form.
sclv
+7  A: 

The GHC extension -XDisambiguateRecordFields will allow foo = Foo { value = 42 } (but not n = value foo).

There's a large body of literature on the shortcomings of Haskell's current record system and candidates for its replacement, as well as a handful of libraries that attempt to provide nicer solutions now. fclabels is the only one that I've used personally.

This StackOverflow question is similar, and some of the answers there might also be useful for you.

This is really a special case of the more general issue of functions in different modules that have the same name but operate on different, unrelated types.

Qualified imports and aliases are usually enough to solve this problem.

Travis Brown