Can someone show a simple example where state monad can be better than passing state directly?
bar1 (Foo x) = Foo (x + 1)
vs
bar2 :: State Foo Foo
bar2 = do
modify (\(Foo x) -> Foo (x + 1))
get
State passing is often tedious, error-prone, and hinders refactoring. For example, try labeling a binary tree or rose tree in postorder:
data RoseTree a = Node a [RoseTree a] deriving (Show)
postLabel :: RoseTree a -> RoseTree Int
postLabel = fst . go 0 where
go i (Node _ ts) = (Node i' ts', i' + 1) where
(ts', i') = gots i ts
gots i [] = ([], i)
gots i (t:ts) = (t':ts', i'') where
(t', i') = go i t
(ts', i'') = gots i' ts
Here I had to manually label states in the right order, pass the correct states along, and had to make sure that both the labels and child nodes are in the right order in the result (note that naive use of foldr or foldl for the child nodes could have easily led to incorrect behavior).
Also, if I try to change the code to preorder, I have to make changes that are easy to get wrong:
preLabel :: RoseTree a -> RoseTree Int
preLabel = fst . go 0 where
go i (Node _ ts) = (Node i ts', i') where -- first change
(ts', i') = gots (i + 1) ts -- second change
gots i [] = ([], i)
gots i (t:ts) = (t':ts', i'') where
(t', i') = go i t
(ts', i'') = gots i' ts
Examples:
branch = Node ()
nil = branch []
tree = branch [branch [nil, nil], nil]
preLabel tree == Node 0 [Node 1 [Node 2 [],Node 3 []],Node 4 []]
postLabel tree == Node 4 [Node 2 [Node 0 [],Node 1 []],Node 3 []]
Contrast the state monad solution:
import Control.Monad.State
import Control.Applicative
postLabel' :: RoseTree a -> RoseTree Int
postLabel' = (`evalState` 0) . go where
go (Node _ ts) = do
ts' <- traverse go ts
i <- get <* modify (+1)
pure (Node i ts')
preLabel' :: RoseTree a -> RoseTree Int
preLabel' = (`evalState` 0) . go where
go (Node _ ts) = do
i <- get <* modify (+1)
ts' <- traverse go ts
pure (Node i ts')
Not only is this code more succinct and easier to write correctly, the logic that results in pre- or postorder labeling is far more transparent.
PS: bonus applicative style:
postLabel' :: RoseTree a -> RoseTree Int
postLabel' = (`evalState` 0) . go where
go (Node _ ts) =
flip Node <$> traverse go ts <*> (get <* modify (+1))
preLabel' :: RoseTree a -> RoseTree Int
preLabel' = (`evalState` 0) . go where
go (Node _ ts) =
Node <$> (get <* modify (+1)) <*> traverse go ts
As an example to my comment above, you can write code using the State monad like
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
import Data.Text (Text)
import qualified Data.Text as Text
import Control.Monad.State
data MyState = MyState
{ _count :: Int
, _messages :: [Text]
} deriving (Eq, Show)
makeLenses ''MyState
type App = State MyState
incrCnt :: App ()
incrCnt = modify (\my -> my & count +~ 1)
logMsg :: Text -> App ()
logMsg msg = modify (\my -> my & messages %~ (++ [msg]))
logAndIncr :: Text -> App ()
logAndIncr msg = do
incrCnt
logMsg msg
app :: App ()
app = do
logAndIncr "First step"
logAndIncr "Second step"
logAndIncr "Third step"
logAndIncr "Fourth step"
logAndIncr "Fifth step"
Note that using additional operators from Control.Lens also lets you write incrCnt and logMsg as
incrCnt = count += 1
logMsg msg = messages %= (++ [msg])
which is another benefit of using State in combination with the lens library, but for the sake of comparison I'm not using them in this example. To write the equivalent code above with just argument passing it would look more like
incrCnt :: MyState -> MyState
incrCnt my = my & count +~ 1
logMsg :: MyState -> Text -> MyState
logMsg my msg = my & messages %~ (++ [msg])
logAndIncr :: MyState -> Text -> MyState
logAndIncr my msg =
let incremented = incrCnt my
logged = logMsg incremented msg
in logged
At this point it isn't too bad, but once we get to the next step I think you'll see where the code duplication really comes in:
app :: MyState -> MyState
app initial =
let first_step = logAndIncr initial "First step"
second_step = logAndIncr first_step "Second step"
third_step = logAndIncr second_step "Third step"
fourth_step = logAndIncr third_step "Fourth step"
fifth_step = logAndIncr fourth_step "Fifth step"
in fifth_step
Another benefit of wrapping this up in a Monad instance is that you can use the full power of Control.Monad and Control.Applicative with it:
app = mapM_ logAndIncr [
"First step",
"Second step",
"Third step",
"Fourth step",
"Fifth step"
]
Which allows for much more flexibility when processing values calculated at runtime compared to static values.
The difference between manual state passing and using the State monad is simply that the State monad is an abstraction over the manual process. It also happens to fit several other widely used more general abstractions, like Monad, Applicative, Functor, and a few others. If you also use the StateT transformer then you can compose these operations with other monads, such as IO. Can you do all of this without State and StateT? Of course you can, and there's no one stopping you from doing so, but the point is that State abstracts this pattern out and gives you access to a huge toolbox of more general tools. Also, a small modification to the types above makes the same functions work in multiple contexts:
incrCnt :: MonadState MyState m => m ()
logMsg :: MonadState MyState m => Text -> m ()
logAndIncr :: MonadState MyState m => Text -> m ()
These will now work with App, or with StateT MyState IO, or any other monad stack with a MonadState implementation. It makes it significantly more reusable than simple argument passing, which is only possible through the abstraction that is StateT.
In my experience, the point of many Monads doesn't really click until you get into larger examples, so here is an example use of State (well, StateT ... IO) to parse an incoming request to a web service.
The pattern is that this web service can be called with a bunch of options of different types, though all except for one of the options have decent defaults. If I get a incoming JSON request with an unknown key value, I should abort with an appropriate message. I use the state to keep track of what the current config is, and what the remainder of the JSON request is, along with a bunch of accessor methods.
(Based on code currently in production, with the names of everything changed and the details of what this service actually does obscured)
{-# LANGUAGE OverloadedStrings #-}
module XmpConfig where
import Data.IORef
import Control.Arrow (first)
import Control.Monad
import qualified Data.Text as T
import Data.Aeson hiding ((.=))
import qualified Data.HashMap.Strict as MS
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.State (execStateT, StateT, gets, modify)
import qualified Data.Foldable as DF
import Data.Maybe (fromJust, isJust)
data Taggy = UseTags Bool | NoTags
newtype Locale = Locale String
data MyServiceConfig = MyServiceConfig {
_mscTagStatus :: Taggy
, _mscFlipResult :: Bool
, _mscWasteTime :: Bool
, _mscLocale :: Locale
, _mscFormatVersion :: Int
, _mscJobs :: [String]
}
baseWebConfig :: IO (IORef [String], IORef [String], MyServiceConfig)
baseWebConfig = do
infoRef <- newIORef []
warningRef <- newIORef []
let cfg = MyServiceConfig {
_mscTagStatus = NoTags
, _mscFlipResult = False
, _mscWasteTime = False
, _mscLocale = Locale "en-US"
, _mscFormatVersion = 1
, _mscJobs = []
}
return (infoRef, warningRef, cfg)
parseLocale :: T.Text -> Maybe Locale
parseLocale = Just . Locale . T.unpack -- The real thing does more
parseJSONReq :: MS.HashMap T.Text Value ->
IO (IORef [String], IORef [String], MyServiceConfig)
parseJSONReq m = liftM snd
(baseWebConfig >>= (\c -> execStateT parse' (m, c)))
where
parse' :: StateT (MS.HashMap T.Text Value,
(IORef [String], IORef [String], MyServiceConfig))
IO ()
parse' = do
let addWarning s = do let snd3 (_, b, _) = b
r <- gets (snd3 . snd)
liftIO $ modifyIORef r (++ [s])
-- These two functions suck a key/value off the input map and
-- pass the value on to the handler "h"
onKey k h = onKeyMaybe k $ DF.mapM_ h
onKeyMaybe k h = do myb <- gets fst
modify $ first $ MS.delete k
h (MS.lookup k myb)
-- Access the "lns" field of the configuration
config setter = modify (\(a, (b, c, d)) -> (a, (b, c, setter d)))
onKey "tags" $ \x -> case x of
Bool True -> config $ \c -> c {_mscTagStatus = UseTags False}
String "true" -> config $ \c -> c {_mscTagStatus = UseTags False}
Bool False -> config $ \c -> c {_mscTagStatus = NoTags}
String "false" -> config $ \c -> c {_mscTagStatus = NoTags}
String "inline" -> config $ \c -> c {_mscTagStatus = UseTags True}
q -> addWarning ("Bad value ignored for tags: " ++ show q)
onKey "reverse" $ \x -> case x of
Bool r -> config $ \c -> c {_mscFlipResult = r}
q -> addWarning ("Bad value ignored for reverse: " ++ show q)
onKey "spin" $ \x -> case x of
Bool r -> config $ \c -> c {_mscWasteTime = r}
q -> addWarning ("Bad value ignored for spin: " ++ show q)
onKey "language" $ \x -> case x of
String s | isJust (parseLocale s) ->
config $ \c -> c {_mscLocale = fromJust $ parseLocale s}
q -> addWarning ("Bad value ignored for language: " ++ show q)
onKey "format" $ \x -> case x of
Number 1 -> config $ \c -> c {_mscFormatVersion = 1}
Number 2 -> config $ \c -> c {_mscFormatVersion = 2}
q -> addWarning ("Bad value ignored for format: " ++ show q)
onKeyMaybe "jobs" $ \p -> case p of
Just (Array x) -> do q <- parseJobs x
config $ \c -> c {_mscJobs = q}
Just (String "test") ->
config $ \c -> c {_mscJobs = ["test1", "test2"]}
Just other -> fail $ "Bad value for jobs: " ++ show other
Nothing -> fail "Missing value for jobs"
m' <- gets fst
unless (MS.null m') (fail $ "Unrecognized key(s): " ++ show (MS.keys m'))
parseJobs :: (Monad m, DF.Foldable b) => b Value -> m [String]
parseJobs = DF.foldrM (\a b -> liftM (:b) (parseJob a)) []
parseJob :: (Monad m) => Value -> m String
parseJob (String s) = return (T.unpack s)
parseJob q = fail $ "Bad job value: " ++ show q
Related
This might seem artificial, but I can't seem to find an obvious answer to the following:
Say I have the following imports:
import qualified Data.Map as M
import qualified Data.HashMap.Lazy as HML
Now I have some function (comp) that takes some list, does something, creates a map, returns it.
My question is how do I have two ways of calling comp so that its calls (say) to insert and size map correctly?
As a strawman, I could write two copies of this function, one referencing M.insert and M.size, while the other references HML.insert and HML.size ... but how do I "pass the module as a parameter", or indicate this otherwise?
Thanks!
Edit: to make this less abstract these are the exact definitions of comp:
mapComp :: KVPairs -> IO ()
mapComp kvpairs = do
let init = M.empty
let m = foldr ins init kvpairs where
ins (k, v) t = M.insert k v t
if M.size m /= length kvpairs
then putStrLn $ "FAIL: " ++ show (M.size m) ++ ", " ++ show (length kvpairs)
else pure ()
hashmapComp :: KVPairs -> IO()
hashmapComp kvpairs = do
let init = HML.empty
let m = foldr ins init kvpairs where
ins (k, v) t = HML.insert k v t
if HML.size m /= length kvpairs
then putStrLn $ "Fail: " ++ show (HML.size m) ++ ", " ++ show (length kvpairs)
else pure ()
Edit (2): this turned out to be way more interesting than I anticipated, thanks to everyone who responded!
Here's how to to it with module signatures and mixins (a.k.a. Backpack)
You would have to define a library (it could be an internal library) with a signature like:
-- file Mappy.hsig
signature Mappy where
class C k
data Map k v
empty :: Map k v
insert :: C k => k -> v -> Map k v -> Map k v
size :: Map k v -> Int
in the same library or in another, write code that imports the signature as if it were a normal module:
module Stuff where
import qualified Mappy as M
type KVPairs k v = [(k,v)]
comp :: M.C k => KVPairs k v -> IO ()
comp kvpairs = do
let init = M.empty
let m = foldr ins init kvpairs where
ins (k, v) t = M.insert k v t
if M.size m /= length kvpairs
then putStrLn $ "FAIL: " ++ show (M.size m) ++ ", " ++ show (length kvpairs)
else pure ()
In another library (it must be a different one) write an "implementation" module that matches the signature:
-- file Mappy.hs
{-# language ConstraintKinds #-}
module Mappy (C,insert,empty,size,Map) where
import Data.Map.Lazy
type C = Ord
The "signature match" is performed based on names and types only, the implementation module doesn't need to know about the existence of the signature.
Then, in a library or executable in which you want to use the abstract code, pull both the library with the abstract code and the library with the implementation:
executable somexe
main-is: Main.hs
build-depends: base ^>=4.11.1.0,
indeflib,
lazyimpl
default-language: Haskell2010
library indeflib
exposed-modules: Stuff
signatures: Mappy
build-depends: base ^>=4.11.1.0
hs-source-dirs: src
default-language: Haskell2010
library lazyimpl
exposed-modules: Mappy
build-depends: base ^>=4.11.1.0,
containers >= 0.5
hs-source-dirs: impl1
default-language: Haskell2010
Sometimes the name of the signature and of the implementing module don't match, in that case one has to use the mixins section of the Cabal file.
Edit. Creating the HashMap implementation proved somewhat tricky, because insert required two constraints (Eq and Hashable) instead of one. I had to resort to the "class synonym" trick. Here's the code:
{-# language ConstraintKinds, FlexibleInstances, UndecidableInstances #-}
module Mappy (C,insert,HM.empty,HM.size,Map) where
import Data.Hashable
import qualified Data.HashMap.Strict as HM
type C = EqHash
class (Eq q, Hashable q) => EqHash q -- class synonym trick
instance (Eq q, Hashable q) => EqHash q
insert :: EqHash k => k -> v -> Map k v -> Map k v
insert = HM.insert
type Map = HM.HashMap
The simplest is to parameterize by the operations you actually need, rather than the module. So:
mapComp ::
m ->
(K -> V -> m -> m) ->
(m -> Int) ->
KVPairs -> IO ()
mapComp empty insert size kvpairs = do
let m = foldr ins empty kvpairs where
ins (k, v) t = insert k v t
if size m /= length kvpairs
then putStrLn $ "FAIL: " ++ show (size m) ++ ", " ++ show (length kvpairs)
else pure ()
You can then call it as, e.g. mapComp M.empty M.insert M.size or mapComp HM.empty HM.insert HM.size. As a small side benefit, callers may use this function even if the data structure they prefer doesn't offer a module with exactly the right names and types by writing small adapters and passing them in.
If you like, you can combine these into a single record to ease passing them around:
data MapOps m = MapOps
{ empty :: m
, insert :: K -> V -> m -> m
, size :: m -> Int
}
mops = MapOps M.empty M.insert M.size
hmops = MapOps HM.empty HM.insert HM.size
mapComp :: MapOps m -> KVPairs -> IO ()
mapComp ops kvpairs = do
let m = foldr ins (empty ops) kvpairs where
ins (k, v) t = insert ops k v t
if size ops m /= length kvpairs
then putStrLn "Yikes!"
else pure ()
I am afraid that it is not possible to do in Haskell without workarounds. Main problem is that comp would use different types for same objects for M and for HML variants, which is impossible to do in Haskell directly.
You will need to let comp know which option are you going to take using either data or polymorphism.
As a base idea I would create ADT to cover possible options and use boolean value to determine the module:
data SomeMap k v = M (M.Map k v) | HML (HML.HashMap k v)
f :: Bool -> IO ()
f shouldIUseM = do ...
And then use case expression in foldr to check whether your underlying map is M or HML. However, I don't see any good point of using such a bloatcode, it would be much better to create compM and compHML separately.
Another approach would be to create typeclass that would wrap all your cases
class SomeMap m where
empty :: m k v
insert :: k -> v -> m k v -> m k v
size :: m k v -> Int
And then write instances for each map manually (or using some TemplateHaskell magic, which I believe could help here, however it is out of my skills). It will require some bloat code as well, but then you will be able to parametrize comp over the used map type:
comp :: SomeMap m => m -> IO ()
comp thisCouldBeEmptyInitMap = do ...
But honestly, I would write this function like this:
comp :: Bool -> IO ()
comp m = if m then fooM else fooHML
I'm a little suspicious this is an XY problem, so here's how I would address the code you linked to. You have, the following:
mapComp :: KVPairs -> IO ()
mapComp kvpairs = do
let init = M.empty
let m = foldr ins init kvpairs where
ins (k, v) t = M.insert k v t
if M.size m /= length kvpairs
then putStrLn $ "FAIL: " ++ show (M.size m) ++ ", " ++ show (length kvpairs)
else pure ()
hashmapComp :: KVPairs -> IO()
hashmapComp kvpairs = do
let init = HML.empty
let m = foldr ins init kvpairs where
ins (k, v) t = HML.insert k v t
if HML.size m /= length kvpairs
then putStrLn $ "Fail: " ++ show (HML.size m) ++ ", " ++ show (length kvpairs)
else pure ()
This has a lot of repetition, which is usually not good. So we factor out the bits that are different between the two functions, and parameterize a new function by those changing bits:
-- didn't try to compile this
comp :: mp k v -> (k -> v -> mp k v -> mp k v) -> (mp k v -> Int) -> KVPairs -> IO()
comp h_empty h_insert h_size kvpairs = do
let init = h_empty
let m = foldr ins init kvpairs where
ins (k, v) t = h_insert k v t
if h_size m /= length kvpairs
then putStrLn $ "Fail: " ++ show (h_size m) ++ ", " ++ show (length kvpairs)
else pure ()
As you can see this is a really mechanical process. Then you call e.g. comp M.empty M.insert M.size.
If you want to be able to define comp such that it can work on map types that you haven't thought of yet (or which your users will specify), then you must define comp against an abstract interface. This is done with typeclasses, as in SomeMap radrow's answer.
In fact you can do part of this abstracting already, by noticing that both maps you want to work with implement the standard Foldable and Monoid.
-- didn't try to compile this
comp :: (Foldable (mp k), Monoid (mp k v))=> (k -> v -> mp k v -> mp k v) -> KVPairs -> IO()
comp h_insert kvpairs = do
let init = mempty -- ...also why not just use `mempty` directly below:
let m = foldr ins init kvpairs where
ins (k, v) t = h_insert k v t
if length m /= length kvpairs
then putStrLn $ "Fail: " ++ show (length m) ++ ", " ++ show (length kvpairs)
else pure ()
As mentioned in the comments, I think backpack is (will be?) the way to get what I think you're asking for, i.e. parameterized modules. I don't know much about it, and it's not clear to me what usecases it solves that you wouldn't want to use the more traditional approach I've described above (maybe I'll read the wiki page).
I would like to generate random sequences from a Markov chain. To generate the Markov chain I use the following code.
module Main where
import qualified Control.Monad.Random as R
import qualified Data.List as L
import qualified Data.Map as M
type TransitionMap = M.Map (String, String) Int
type MarkovChain = M.Map String [(String, Int)]
addTransition :: (String, String) -> TransitionMap -> TransitionMap
addTransition k = M.insertWith (+) k 1
fromTransitionMap :: TransitionMap -> MarkovChain
fromTransitionMap m =
M.fromList [(k, frequencies k) | k <- ks]
where ks = L.nub $ map fst $ M.keys m
frequencies a = map reduce $ filter (outboundFor a) $ M.toList m
outboundFor a k = fst (fst k) == a
reduce e = (snd (fst e), snd e)
After collecting the statistics and generating a Markov Chain object I would like to generate random sequences. I could imagine this method could look something like that (pseudo-code)
generateSequence mc s
| s == "." = s
| otherwise = s ++ " " ++ generateSequence mc s'
where s' = drawRandomlyFrom $ R.fromList $ mc ! s
I would greatly appreciate if someone could explain to me, how I should implement this function.
Edit
If anyone's interested it wasn't as difficult as I thought.
module Main where
import qualified Control.Monad.Random as R
import qualified Data.List as L
import qualified Data.Map as M
type TransitionMap = M.Map (String, String) Rational
type MarkovChain = M.Map String [(String, Rational)]
addTransition :: TransitionMap -> (String, String) -> TransitionMap
addTransition m k = M.insertWith (+) k 1 m
fromTransitionMap :: TransitionMap -> MarkovChain
fromTransitionMap m =
M.fromList [(k, frequencies k) | k <- ks]
where ks = L.nub $ map fst $ M.keys m
frequencies a = map reduce $ filter (outboundFor a) $ M.toList m
outboundFor a k = fst (fst k) == a
reduce e = (snd (fst e), snd e)
generateSequence :: (R.MonadRandom m) => MarkovChain -> String -> m String
generateSequence m s
| not (null s) && last s == '.' = return s
| otherwise = do
s' <- R.fromList $ m M.! s
ss <- generateSequence m s'
return $ if null s then ss else s ++ " " ++ ss
fromSample :: [String] -> MarkovChain
fromSample ss = fromTransitionMap $ foldl addTransition M.empty $ concatMap pairs ss
where pairs s = let ws = words s in zipWith (,) ("":ws) ws
sample :: [String]
sample = [ "I am a monster."
, "I am a rock star."
, "I want to go to Hawaii."
, "I want to eat a hamburger."
, "I have a really big headache."
, "Haskell is a fun language."
, "Go eat a big hamburger."
, "Markov chains are fun to use."
]
main = do
s <- generateSequence (fromSample sample) ""
print s
The only tiny annoyance is the fake "" starting node.
Not sure if this is what you're looking for. This compiles though:
generateSequence :: (R.MonadRandom m) => MarkovChain -> String -> m String
generateSequence mc s | s == "." = return s
| otherwise = do
s' <- R.fromList $ rationalize (mc M.! s)
s'' <- generateSequence mc s'
return $ s ++ " " ++ s''
rationalize :: [(String,Int)] -> [(String,Rational)]
rationalize = map (\(x,i) -> (x, toRational i))
All random number generation needs to happen in either the Random monad or the IO monad. For your purpose, it's probably easiest to understand how to do that in the IO monad, using evalRandIO. In the example below, getRandom is the function we want to use. Now getRandom operates in the Random monad, but we can use evalRandIO to lift it to the IO monad, like this:
main :: IO ()
main = do
x <- evalRandIO getRandom :: IO Double
putStrLn $ "Your random number is " ++ show x
Note: The reason we have to add the type signature to the line that binds x is because in this particular example there are no other hints to tell the compiler what type we want x to be. However, if we used x in some way that makes it clear that we want it to be a Double (e.g., multiplying by another Double), then the type signature wouldn't be necessary.
Using your MarkovChain type, for a current state you can trivially get the available transitions in the form [(nextState,probability)]. (I'm using the word "probability" loosely, it doesn't need to be a true probability; any numeric weight is fine). This is what fromList in Control.Monad.Random is designed for. Again, it operates in the Random monad, but we can use evalRandIO to lift it to the IO monad. Suppose transitions is your list of transitions, having the type [(nextState,probability)]. Then, in the IO monad you can call:
nextState <- evalRandIO $ fromList transitions
You might instead want to create your own function that operates in the Random monad, like this:
getRandomTransition :: RandomGen g => MarkovChain -> String -> Rand g String
getRandomTransition currState chain = do
let transitions = lookup currState chain
fromList transitions
Then you can call this function in the IO monad using evalRandIO, e.g.
nextState <- evalRandIO $ getRandomTransition chain
I'm trying to understand what happens in the following code, the code behaves properly, but I'm trying to understand why.
import Control.Monad.State
import System.IO
import System.Environment
echoArgs :: [String] -> State Int [String]
echoArgs x = loopArgs x >> return x
where loopArgs [] = return ()
loopArgs s#(x':xs') = modify (+1) >> loopArgs xs'
main :: IO ()
main = do
argv <- getArgs
let s = echoArgs argv
mapM_ putStr' (evalState s 0)
putStrLn $ "\nNum Args = " ++ show (execState s 0)
where putStr' x = putStr $ x ++ " "
What I'm not understanding is why the state of the State monad does not get 'reset' with each successive call to loopArgs. Does the state get passed as a variable, with each >> and if so could someone show me how?
Does the state get passed as a variable, with each >> and if so could someone show me how?
It does indeed. It's helpful to look at a toy implementation of the State monad.
newtype State s a = State { runState :: s -> (a,s) }
instance Monad (State s) where
return a = State $ \s -> (a, s)
State act >>= k = State $ \s ->
let (a, s') = act s
in runState (k a) s'
get :: State s s
get = State $ \s -> (s, s)
put :: s -> State s ()
put s = State $ \_ -> ((), s)
modify :: (s -> s) -> State s ()
modify f = get >>= \x -> put (f x)
When you bind using >>= or >> the accumulated state is threaded through as an argument to the function on the right hand side.
When you run execState or evalState it then just extracts either the resulting value or the state from the resulting tuple.
execState :: State s a -> s -> s
execState act = snd . runState act
evalState :: State s a -> s -> a
evalState act = fst . runState act
In this response to another question, a little Haskell code sketch was given which uses wrapper functions to factor out some code for doing syntax checking on command line arguments. Here's the part of the code which I'm trying to simplify:
takesSingleArg :: (String -> IO ()) -> [String] -> IO ()
takesSingleArg act [arg] = act arg
takesSingleArg _ _ = showUsageMessage
takesTwoArgs :: (String -> String -> IO ()) -> [String] -> IO ()
takesTwoArgs act [arg1, arg2] = act arg1 arg2
takesTwoArgs _ _ = showUsageMessage
Is there a way (maybe using Template Haskell?) to avoid having to write extra functions for each number of arguments? Ideally, I'd like to be able to write something like (I'm making this syntax up)
generateArgumentWrapper<2, showUsageMessage>
And that expands to
\fn args -> case args of
[a, b] -> fn a b
_ -> showUsageMessage
Ideally, I could even have a variable number of arguments to the generateArgumentWrapper meta-function, so that I could do
generateArgumentWrapper<2, asInt, asFilePath, showUsageMessage>
And that expands to
\fn args -> case args of
[a, b] -> fn (asInt a) (asFilePath b)
_ -> showUsageMessage
Is anybody aware of a way to achieve this? It would be a really easy way to bind command line arguments ([String]) to arbitrary functions. Or is there maybe a totally different, better approach?
Haskell has polyvariadic functions. Imagine you had a type like
data Act = Run (String -> Act) | Res (IO ())
with some functions to do what you want
runAct (Run f) x = f x
runAct (Res _) x = error "wrong function type"
takeNargs' 0 (Res b) _ = b
takeNargs' 0 (Run _) _ = error "wrong function type"
takeNargs' n act (x:xs) = takeNargs' (n-1) (runAct act x) xs
takeNargs' _ _ [] = error "not long enough list"
now, all you you need is to marshal functions into this Act type. You need some extensions
{-# LANGUAGE FlexibleInstances, FlexibleContexts #-}
and then you can define
class Actable a where
makeAct :: a -> Act
numberOfArgs :: a -> Int
instance Actable (String -> IO ()) where
makeAct f = Run $ Res . f
numberOfArgs _ = 1
instance Actable (b -> c) => Actable (String -> (b -> c)) where
makeAct f = Run $ makeAct . f
numberOfArgs f = 1 + numberOfArgs (f "")
now you can define
takeNArgs n act = takeNargs' n (makeAct act)
which makes it easier to define your original functions
takesSingleArg :: (String -> IO ()) -> [String] -> IO ()
takesSingleArg = takeNArgs 1
takesTwoArgs :: (String -> String -> IO ()) -> [String] -> IO ()
takesTwoArgs = takeNArgs 2
But we can do even better
takeTheRightNumArgs f = takeNArgs (numberOfArgs f) f
Amazingly, this works (GHCI)
*Main> takeTheRightNumArgs putStrLn ["hello","world"]
hello
*Main> takeTheRightNumArgs (\x y -> putStrLn x >> putStrLn y) ["hello","world"]
hello
world
Edit: The code above is much more complicated than it needs to be. Really, all you want is
class TakeArgs a where
takeArgs :: a -> [String] -> IO ()
instance TakeArgs (IO ()) where
takeArgs a _ = a
instance TakeArgs a => TakeArgs (String -> a) where
takeArgs f (x:xs) = takeArgs (f x) xs
takeArgs f [] = error "end of list"
You might want to make use of existing libraries to deal with command line arguments. I believe the de-facto standard right now is cmdargs, but other options exist, such as ReadArgs and console-program.
Combinators are your friend. Try this:
take1 :: [String] -> Maybe String
take1 [x] = Just x
take1 _ = Nothing
take2 :: [String] -> Maybe (String,String)
take2 [x,y] = Just (x,y)
take2 _ = Nothing
take3 :: [String] -> Maybe ((String,String),String)
take3 [x,y,z] = Just ((x,y),z)
take3 _ = Nothing
type ErrorMsg = String
with1 :: (String -> IO ()) -> ErrorMsg -> [String] -> IO ()
with1 f msg = maybe (fail msg) f . take1
with2 :: (String -> String -> IO ()) -> ErrorMsg -> [String] -> IO ()
with2 f msg = maybe (fail msg) (uncurry f) . take2
with3 :: (String -> String -> String -> IO ()) -> ErrorMsg -> [String] -> IO ()
with3 f msg = maybe (fail msg) (uncurry . uncurry $ f) . take3
foo a b c = putStrLn $ a ++ " :: " ++ b ++ " = " ++ c
bar = with3 foo "You must send foo a name, type, definition"
main = do
bar [ "xs", "[Int]", "[1..3]" ]
bar [ "xs", "[Int]", "[1..3]", "What am I doing here?" ]
And if you like overpowered language extensions:
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, UndecidableInstances #-}
foo a b c = putStrLn $ a ++ " :: " ++ b ++ " = " ++ c
foo_msg = "You must send foo a name, type, definition"
class ApplyArg a b | a -> b where
appArg :: ErrorMsg -> a -> [String] -> IO b
instance ApplyArg (IO b) b where
appArg _msg todo [] = todo
appArg msg _todo _ = fail msg
instance ApplyArg v q => ApplyArg (String -> v) q where
appArg msg todo (x:xs) = appArg msg (todo x) xs
appArg msg _todo _ = fail msg
quux :: [String] -> IO ()
quux xs = appArg foo_msg foo xs
main = do
quux [ "xs", "[int]", "[1..3]" ]
quux [ "xs", "[int]", "[1..3]", "what am i doing here?" ]
The concrete examples at http://www.haskell.org/haskellwiki/State_Monad are very helpful in understanding how to write real code with monads (see also stackoverflow/9014218). But most of us new students come from an OO background, so mapping an OO program to haskell will help to demonstrate how to write an equivalent haskell code. (Yes, the two paradigms are entirely different, and it is not wise to translate an OO-style code directly to haskell, but just this once as a tutorial.)
Here is an OO-style code, which creates 2 instances of an object, and then calls member functions which modify their respective member variables, and finally prints them. How do we write this using haskell state monads?
class A:
int p;
bool q;
A() { p=0; q=False;} // constructor
int y() { // member function
if(q) p++; else p--;
return p;
}
bool z() { // member function
q = not q;
return q;
}
main:
// main body - creates instances and calls member funcs
a1 = A; a2 = A; // 2 separate instances of A
int m = a1.y();
m = m + a1.y();
bool n = a2.z();
print m, n, a1.p, a1.q, a2.p, a2.q;
A direct translation would be something like:
module Example where
import Control.Monad.State
data A = A { p :: Int, q :: Bool }
-- constructor
newA :: A
newA = A 0 False
-- member function
y :: State A Int
y = do
b <- getQ
modifyP $ if b then (+1) else (subtract 1)
getP
-- member function
z :: State A Bool
z = do
b <- gets q
modifyQ not
getQ
main :: IO ()
main = do
let (m,a1) = flip runState newA $ do
m <- y
m <- (m +) `fmap` y
return m
let (n,a2) = flip runState newA $ do
n <- z
return n
print (m, n, p a1, q a1, p a2, q a2)
-- general purpose getters and setters
getP :: State A Int
getP = gets p
getQ :: State A Bool
getQ = gets q
putP :: Int -> State A ()
putP = modifyP . const
putQ :: Bool -> State A ()
putQ = modifyQ . const
modifyP :: (Int -> Int) -> State A ()
modifyP f = modify $ \a -> a { p = f (p a) }
modifyQ :: (Bool -> Bool) -> State A ()
modifyQ f = modify $ \a -> a { q = f (q a) }
And I probably wouldn't bother with the manual getter/setters and just use lenses.
{-# LANGUAGE TemplateHaskell, FlexibleContexts #-}
module Main where
import Control.Applicative
import Control.Monad.State
import Data.Lenses
import Data.Lenses.Template
data A = A { p_ :: Int, q_ :: Bool } deriving Show
$( deriveLenses ''A )
-- constructor
newA :: A
newA = A 0 False
-- member function
y :: MonadState A m => m Int
y = do
b <- q get
if b then p $ modify (+1) else p $ modify (subtract 1)
p get
-- member function
z :: MonadState A m => m Bool
z = do
q $ modify not
q get
data Main = Main { a1_ :: A, a2_ :: A, m_ :: Int, n_ :: Bool } deriving Show
$( deriveLenses ''Main )
main :: IO ()
main = do
-- main body - creates instances and calls member funcs
print $ flip execState (Main undefined undefined undefined undefined) $ do
a1 $ put newA ; a2 $ put newA -- 2 separate instances of A
m . put =<< a1 y
m . put =<< (+) <$> m get <*> a1 y
n . put =<< a2 z
But that's not what I'd really write, because I'm bending the Haskell over backward to try and mimic the OO-style. So it just looks awkward.
To me, the real purpose of object oriented code is to program to an interface. When I'm using these kinds of objects, I can rely on them to support these kinds of methods. So, in haskell, I'd do that using a type class:
{-# LANGUAGE TemplateHaskell, FlexibleContexts #-}
module Main where
import Prelude hiding (lookup)
import Control.Applicative
import Control.Monad.State
import Data.Lenses
import Data.Lenses.Template
import Data.Map
class Show a => Example a where
-- constructor
new :: a
-- member function
y :: MonadState a m => m Int
-- member function
z :: MonadState a m => m Bool
data A = A { p_ :: Int, q_ :: Bool } deriving Show
$( deriveLenses ''A )
instance Example A where
new = A 0 False
y = do
b <- q get
if b then p $ modify (+1) else p $ modify (subtract 1)
p get
z = do
q $ modify not
q get
data B = B { v_ :: Int, step :: Map Int Int } deriving Show
$( deriveLenses ''B )
instance Example B where
new = B 10 . fromList $ zip [10,9..1] [9,8..0]
y = v get
z = do
i <- v get
mi <- lookup i `liftM` gets step
case mi of
Nothing -> return False
Just i' -> do
v $ put i'
return True
data Main a = Main { a1_ :: a, a2_ :: a, m_ :: Int, n_ :: Bool } deriving Show
start :: Example a => Main a
start = Main undefined undefined undefined undefined
$( deriveLenses ''Main )
run :: Example a => State (Main a) ()
run = do
-- main body - creates instances and calls member funcs
a1 $ put new ; a2 $ put new -- 2 separate instances of a
m . put =<< a1 y
m . put =<< (+) <$> m get <*> a1 y
n . put =<< a2 z
main :: IO ()
main = do
print $ flip execState (start :: Main A) run
print $ flip execState (start :: Main B) run
So now I can reuse the same run for different types A and B.
The State monad cannot be used to emulate classes. It is used to model state that "sticks" to the code that you're running, and not state that is "independent" and residing in object-oriented classes.
If you don't want method overriding and inheritance, the closest you can get to OOP classes in Haskell is to use records with associated functions. The only difference you have to be aware of in that case is that all "class methods" return new "objects", they don't modify the old "object".
For example:
data A =
A
{ p :: Int
, q :: Bool
}
deriving (Show)
-- Your "A" constructor
newA :: A
newA = A { p = 0, q = False }
-- Your "y" method
y :: A -> (Int, A)
y a =
let newP = if q a then p a + 1 else p a - 1
newA = a { p = newP }
in (newP, newA)
-- Your "z" method
z :: A -> Bool
z = not . q
-- Your "main" procedure
main :: IO ()
main =
print (m', n, p a1'', q a1'', p a2, q a2)
where
a1 = newA
a2 = newA
(m, a1') = y a1
(temp, a1'') = y a1'
m' = m + temp
n = z a2
This program prints:
(-3,True,-2,False,0,False)
Note that we had to create new variables to store the new versions of m and a1 (I just added ' at the end each time). Haskell does not have language-level mutable variables, so you shouldn't try to use the language for that.
It is possible to make mutable variables via the use of IO references.
Note, however, that the following code is considered extremely bad coding style among Haskellers. If I was a teacher and had a student who wrote code like this, I'd not give a passing grade on the assignment; if I employed a Haskell programmer who wrote code like this, I'd consider firing him if he didn't have a VERY good reason for writing code like this.
import Data.IORef -- IO References
data A =
A
{ p :: IORef Int
, q :: IORef Bool
}
newA :: IO A
newA = do
p' <- newIORef 0
q' <- newIORef False
return $ A p' q'
y :: A -> IO Int
y a = do
q' <- readIORef $ q a
if q'
then modifyIORef (p a) (+ 1)
else modifyIORef (p a) (subtract 1)
readIORef $ p a
z :: A -> IO Bool
z = fmap not . readIORef . q
main :: IO ()
main = do
a1 <- newA
a2 <- newA
m <- newIORef =<< y a1
modifyIORef m . (+) =<< y a1
n <- z a2
m' <- readIORef m
pa1 <- readIORef $ p a1
qa1 <- readIORef $ q a1
pa2 <- readIORef $ p a2
qa2 <- readIORef $ q a2
print (m', n, pa1, qa1, pa2, qa2)
This program does the same thing as the above program, but with mutable variables. Again, don't write code like this except for very rare circumstances.