[Haskell-cafe] use of modules to save typing
Michael Mossey
mpm at alumni.caltech.edu
Thu Jul 8 04:08:21 EDT 2010
I'm fairly beginnerish to Haskell, and come from OO. I have a complaint
about Haskell, but I think I found a good solution. Any suggestions welcome.
I have RSI and like to minimize typing. The use of classes as name
spaces helps to do that. Also I can use some Emacs abbreviation magic
easily with OO and not so easily with Haskell. I'll explain in a second.
In Haskell, when defining data for complex programs I like to use named
fields to allow for changing data definitions without having to change
all code. But named fields are top-level functions (I think). They must
be chosen not to clash.
My habit has been to prefix them with the name of the constructor. So in
a program for playing back musical documents that needs to track some
state, we have:
data PlayState = PlayState
{ playState_cursor :: Int
, playState_verts :: [Loc]
, playState_len :: Int
, playState_doc :: MusDoc
}
Note all these "playState_" prefixes. Lots of typing, which is not good.
In OO, you could type
x.cursor()
In Haskell you have to type
playState_cursor x
which also, I feel, is harder to read.
Now suppose I want to use PlayState with a State monad.
-- Increment the cursor.
incrCursor :: State PlayState ()
incrCursor =
cur <- gets playState_cursor
len <- gets playState_len
let newCur = min (cur+1) (len-1)
p <- get
put $ p {playState_cursor = newCur}
Okay, I'm sorry, that is just a lot of typing for what it is doing. Not
good for people with RSI, and not all that readable.
I could define a function to make modifying the state a little easier.
playState_update_cursor :: Int -> PlayState -> PlayState
playState_update_cursor i p = p {playState_cur=i}
Then incrCursor would look like:
incrCursor :: State PlayState ()
incrCursor =
cur <- gets playState_cursor
len <- gets playState_len
let newCur = min (cur+1) (len-1)
modify (playState_update_cursor newCur)
Notice how often the characters "playState_" get typed. This would be a
great situation for Emacs abbreviations. When you define an abbreviation
in Emacs, such as defining "xps" to expand to "PlayState", emacs will
watch for the characters xps. It will then replace "xps" with
"PlayState" when you type a non-alphanumeric character following "xps".
So if I type "xps." the moment I hit "." it changes to "PlayState."
But I would have a hard time using this feature with "playState_"
because it is always followed by an alphanumeric character.
So my idea, now, is to put the definition of PlayState in its own module
and import it qualified as PlayState.
---------------- module PlayState --------------
data PlayState = PlayState
{ cursor :: Int
, verts :: [Loc]
, len :: [Int]
, doc :: MusDoc
}
update_cursor i p = p {cursor = i}
-----------------------------------------------
I got rid of the "playState_" prefixes because I am not worried about
using generic field names like "doc". They won't clash if I always
import this qualified. And that reduces the necessary typing in the
definition.
Now my monad looks like
testMonad = do
cursor <- gets PlayState.cursor
len <- gets PlayState.len
let newCur = min (cur+1) (len-1)
modify $ PlayState.update_cursor newCur
Now I can define an abbreviation for PlayState. This is a big help.
Also, I find this more readable. To me
PlayState.cursor
is more readable than
playState_cursor
For one thing, syntax highlighting helps in the former case. For
another, the latter case requires that you recognize a naming
convention, but the former case says clearly: "cursor is within the
namespace PlayState, so this combination must be describing a cursor
related to PlayState."
More information about the Haskell-Cafe
mailing list