It isn't clear what it is that you are trying to generalize the code to do. If you are trying to generalize it to work with an arbitrary input/output stream of lines, then unless you are doing arbitrary I/O it seems to me that all of these instance declarations are overkill. All that you need is to know how to get a line from the stream, and how to send a line.

Assuming that this is the case, you have a couple of options. If you are only going to write to the stream within runCmd, then I'd just pass in the line writing function as an extra argument:


type PDState = StateT PD

loop :: (m String) -> (String -> m ()) -> PDState m a
loop fetchLine sendLine = forever $ lift fetchLine >>= runCmd (lift sendLine)

runCmd :: (String -> PDState m ()) -> PDstate m ()
runCmd sendLine cmd =
        case cmd of
                "Inc" -> increment
                "PrintCount" -> getCount >>= sendLine . show
                "PrintList" -> getList >>= sendLine . show
                ...

If you forsee doing reading and writing at other points in your code, you could use the RWS monad to supply your code not only with a state but also with an environment with the reading and writing functions:


data StreamFunctions m = StreamFunctions
        {       streamLineFetcher :: m String
        ,       streamLineSender :: String -> m ()
        }

fetchLineFromStream = lift $ asks streamLineFetcher
sendLineDownStream cmd = lift (asks streamLineSender >>= return . ($ cmd))

data PDMonad = RWST (StreamFunctions m) () PD m

main = evalRWST loop (StreamFunctions ...) (PD { pdCount = 0, pdList = [] })

loop :: PDMonad m ()
loop = forever $ fetchLineFromStream >>= runCmd

runCmd :: String -> PDMonad m ()
runCmd "Inc" = increment
runCmd "PrintCount" = getCount >>= sendLineDownStream
runCmd "PrintList" = getList >>= sendLineDownStream


Note that we didn't have to put any additional constraints on the monad type variable "m", because other than the fact that we can get a line and send a line within it we don't otherwise care what it is. If you want to do other arbitrary I/O within this framework, though, then you will need to add a "MonadIO m" constraint.


Cheers,
Greg


On Oct 5, 2009, at 8:54 PM, Floptical Logic wrote:

Instead of specifying the monad implementation, specify the interface.
That is, you are using state operations (from MonadState) and IO
operations (from MonadIO). Try removing all the type signatures that
mention PDState and see what you get.

E.g., loop :: (MonadState PD m, MonadIO m) => m a

If I were to make an instance of MonadIO be a parameter to StateT and
I wanted to use the Net monad (from Roll your own IRC bot on the wiki)
with it, I would need to make Net an instance of MonadIO.  What would
this instance look like?

I think the loop function is the least of my worries.  I am more
concerned about the runCmd function.  What would go in place of print
in runCmd?

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

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

Reply via email to