For working with record fields inside of state monads I would recommend trying out one of these packages:
lenses
fclabels
data-accessor
(I think I'm forgetting a couple)

They all have special mechanisms for working with record fields inside state monads (and have lots of other cool stuff)
I'm partial to lenses (I wrote it :) ), but the others are quite good as well.

Hmmm I just noticed that hackage is not generating the documentation for latest version of lenses. I shall have to find out why.
In the meantime, the documentation for the 0.1.2 version is essentially the same.

- Job


On Thu, Jul 8, 2010 at 4:08 AM, Michael Mossey <mpm@alumni.caltech.edu> wrote:
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