[Haskell-cafe] Re: Library design question

Christian Maeder Christian.Maeder at dfki.de
Fri Sep 19 04:35:03 EDT 2008

Duncan Coutts wrote:
> On Thu, 2008-09-18 at 15:43 -0300, Andre Nathan wrote:
>> My Graph type is the following.
>>   data Graph a b = Graph
>>     { adjacencies :: Map Int (a, (Map Int b))
>>     , numVertices :: Int
>>     , numEdges    :: Int
>>     }
>>   addVertex :: Int -> a -> State (Graph a b) ()
>>   addVertex vertex label = do
>>     g <- get
>>     let adj = Map.insert vertex (label, Map.empty) (adjacencies g)
>>     put $ g { adjacencies = adj, numVertices = numVertices g + 1 }
>> So I'm confused right now about how I should proceed. Any hints
>> regarding that?
> To be honest I would not bother with the state monad and just make them
> pure operations returning new graphs:
> addVertex :: Int -> a -> Graph a b -> Graph a b
> It's very common to have this style, ie returning a new/updated
> structure explicitly rather than implicitly in a state monad. Just look
> at the Data.Map api for example. If you later want to stick it in a
> state monad then that would be straightforward but also easy to use
> directly.

I agree. Duncan's version also looks more atomic to me, because Andre's
version (that's renamed to addVertexM below) could be easily derived by:

  addVertexM v l = modify (addVertex v l)

The opposite derivation is also possible but does additional wrapping
into and out of a state:

  addVertex v l = execState (addVertexM v l)

(Furthermore the module Control.Monad.State is "non-portable", because
get, put, modify and gets come in via the MonadState class, but separate
not overloaded versions for these function would make sense in the same
way we have "map" despite "fmap".)

Cheers Christian

More information about the Haskell-Cafe mailing list