
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 10/3/10 00:20 , Amy de Buitléir wrote:
I've written a very simple piece of code in order to help me understand Monads better:
---------- import "mtl" Control.Monad.State
countAnotherSheep :: Int -> (String, Int) countAnotherSheep n = (show n ++ " sheep", n+1)
sheepCounter = State { runState = countAnotherSheep } ----------
So far so good. Now I can do things like:
ghci> evalState sheepCounter 1 "1 sheep" ghci> evalState sheepCounter 2 "2 sheep"
ghci> execState sheepCounter 1 2 ghci> execState sheepCounter 2 3
ghci> evalState (sequence $ replicate 5 sheepCounter) 1 ["1 sheep","2 sheep","3 sheep","4 sheep","5 sheep"]
But what I really want to do is create a main function in which I:
1. Count some sheep. 2. Do something else. 3. Count some more sheep, picking up where I left off.
I could thread the state like this:
increment (_, n) = countAnotherSheep n
main = do let c = countAnotherSheep 1 putStrLn $ fst c let c2 = increment c putStrLn $ fst c2 putStrLn "Now I'll do some other stuff" let c3 = increment c2 putStrLn $ fst c3
... but shouldn't the State monad make threading the state unnecessary? It seems like there should be a way to do this, but I can't figure it out. Thank you in advance for any suggestions.
You're overthinking it. The State monad is defined in terms of those operations precisely so it can hide/abstract them. The way you actually use it is:
countSheepAndStuff :: State Int () countSheepAndStuff = do countAnotherSheep 1 doSomethingElsePossiblyCountingMoreSheep countAnotherSheep 1 -- countAnotherSheep 1 >> doSomethingElse... >> countAnotherSheep 1
countAnotherSheep :: Int -> State Int () countAnotherSheep howmany = modify (+ howmany) -- or: countAnotherSheep = modify . (+)
doSomethingElsePossiblyCountingMoreSheep :: State Int () doSomethingElsePossiblyCountingMoreSheep = undefined -- you tell me :)
main = do -- this throws out the () and keeps the final state/sheep count let sheep = execState countSheepAndStuff 0 putStrLn $ show sheep ++ " sheep total"
You never construct State values manually; while runState looks like a record accessor (and in fact is), it's actually used to send something through a State monad without ever actually creating an explicit State. The trick here is that the accessor runState's type is a function (s -> (a,s)); if you pass a function of the appropriate type, you are implicitly creating a State record, and if you then use the methods and the monad machinery (do or (>>=)/(>>)) you never need to create or touch an actual State value anywhere.
runState (modify (+1) >> modify (+3)) 0
The type of the parameter to runState here is State Integer () (which is really Integer -> ((),Integer)), and then we pass an Integer to that function to kick things off. The Integers come from defaulting based on the 0, 1, and 3, and the () from the definition of the modify function (modify :: s -> State s (), with s set to Integer by aforementioned defaulting). The result is the tuple ((),4). Since you don't need or care about the (), you probably would use execState instead of runState in practice. For the countAnotherSheep you used in your example, constructing lists of Strings, you may want to consider another monad: Writer. When you get to that point, you can explore how to do it with a monad transformer WriterT [String] (State Int) () or the reverse transformer StateT Int (Writer [String]) (), and the situations under which you would want to prefer one or the other. - -- brandon s. allbery [linux,solaris,freebsd,perl] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.10 (Darwin) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ iEYEARECAAYFAkyoEv0ACgkQIn7hlCsL25XorQCghdnWCqRpToRLMB6rnCtZX/nX lAoAnRlVQihEBsH8NRLWzaJUxUf3vZz4 =pqwT -----END PGP SIGNATURE-----