Records in Haskell
Christopher Done
chrisdone at googlemail.com
Thu Sep 15 12:00:28 CEST 2011
I added my evaluation of the module-based approach to existing
records, but on second thoughts it's maybe inappropriate, so I'll post
it here. I saw that some people commented on the reddit discussion
that the solution is to put your types in separate modules but it
doesn't seem that anyone has tried to do this on a large scale. I
tried it (and some other record paradigms including Has), but I don't
think it scales. Here's why…
Suppose I have 112 hand-crafted data types in my
project (e.g. see attachment 51369.txt[1]), this creates a lot of
conflicts in field names and constructor names. For example:
{{{
data Comment = Comment {
commentId :: CommentId
, commentContent :: Content
, commentReviewId :: ReviewId
, commentSubmissionId :: SubmissionId
, commentConferenceId :: ConferenceId
, commentDate :: ISODate
, commentReviewerNumber :: Int
} deriving (Show)
}}}
This is a real type in my project. It has fields like “id”, “content”,
“reviewId”, “submissionId”, “date”. There are seven other data types
that have a field name “submissionId”. There are 15 with
“conferenceId”. There are 7 with “content”. And so on. This is just to
demonstrate that field clashes ''do'' occur ''a lot'' in a nontrivial
project.
It also demonstrates that if you propose to put each of these 112 types
into a separate module, you are having a laugh. I tried this around
the 20 type mark and it was, apart from being very slow at compiling,
''very'' tedious to work with. Creating and editing these modules was a
distracting and pointless chore.
It ''also'' demonstrated, to me, that qualified imports are horrible
when used on a large scale. It happened all the time, that'd I'd
import, say, 10 different data types all qualified. Typing map
(Foo.id . BarMu.thisField) and foo Bar.Zot{x=1,y=2} becomes tedious
and distracting, especially having to add every type module when I
want to use a type. And when records use other types in other modules,
you have ''a lot'' of redundancy. With the prefixing paradigm I'd write
fooId and barMuThisField, which is about as tedious but there is at
least less . confusion and no need to make a load of modules and
import lines. Perhaps local modules would solve half of this
problem. Still have to write “Bar.mu bar” rather than “mu bar”, but
it'd be an improvement.
I also have 21 Enum types which often conflict. I end up having to
include the name of the type in the constructor, or rewording it
awkwardly. I guess I should put these all in separate modules and
import qualified,
too. Tedious, though. At least in this case languages like C# and
Java also require that you type EnumName.EnumValue, so c‘est la vie.
[1]: http://hackage.haskell.org/trac/ghc/attachment/wiki/Records/51369.txt
More information about the Glasgow-haskell-users
mailing list