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."





_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to