I was reading Learn You a Haskell's guide on the state monad, but I had trouble understanding it since the stack example couldn't compile. In the guide, he used the following piece of code:
import Control.Monad.State
type Stack = [Int]
pop :: State Stack Int
pop = State $ \(x:xs) -> (x,xs)
push :: Int -> State Stack ()
push a = State $ \xs -> ((),a:xs)
While I understand what it's supposed to do, it won't compile. I have no idea why. The error message is:
Stack.hs:6:7: Not in scope: data constructor `State'
Stack.hs:9:10: Not in scope: data constructor `State'
This makes no sense to me, since "State" is, to my knowledge, in fact a data constructor, defined as
newtype State s a = State { runState :: s -> (a,s) }
Is the guide "wrong", and if so, how do I fix it?
The state monad is a built in monad in Haskell that allows for chaining of a state variable (which may be arbitrarily complex) through a series of function calls, to simulate stateful code. It is defined as: newtype State s a = State { runState :: (s -> (a,s)) }
One thing I noticed was that Tuple does not have a Monad instance. Which already extremely heavily restricts what we can make the Monad instance be.
monads are used to address the more general problem of computations (involving state, input/output, backtracking, ...) returning values: they do not solve any input/output-problems directly but rather provide an elegant and flexible abstraction of many solutions to related problems.
The I/O monad contains primitives which build composite actions, a process similar to joining statements in sequential order using `;' in other languages. Thus the monad serves as the glue which binds together the actions in a program.
As I mentioned in comments, you ought to use state
instead of State
.
The problem is that State
is not standalone data type (or rather newtype
), but it is the StateT
transformer applied to Identity
monad. Actually, it's defined as
type State s = StateT s Indentity
and because it's just type
synonym, it cannot have State
constructor. That's why Control.Monad.State
uses state
.
The accepted answer already mentions to use the state
function from Control.Monad.State
rather than the State
type. However, if you simply try the accepted answer in ghci
with the associated mtl
package loaded, it will still fail:
Prelude Control.Monad.State> push a = state $ \xs -> ((), a:xs)
<interactive>:5:1: error:
• Non type-variable argument in the constraint: MonadState [a] m
(Use FlexibleContexts to permit this)
• When checking the inferred type
push :: forall a (m :: * -> *). MonadState [a] m => a -> m ()
To resolve this, there are two options here:
FlexibleContexts
compiler extension, as mentioned in the error.We can specify the type signature:
module LearnYouAHaskell where
import Control.Monad.State as State
push :: Int -> State.State [Int] ()
push a = State.state $ \xs -> ((), a:xs)
The above compiles fine and functions as expected.
Or we can add the language extension so it can infer the function type.
{-# LANGUAGE FlexibleContexts #-}
module LearnYouAHaskell where
import Control.Monad.State as State
push a = State.state $ \xs -> ((), a:xs)
Which will also work fine.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With