Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The state monad and learnyouahaskell.com

Tags:

haskell

monads

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?

like image 811
Undreren Avatar asked Mar 14 '12 08:03

Undreren


People also ask

What is a state monad?

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)) }

Is a tuple a monad?

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.

What can you do with monads?

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.

How does the IO monad work?

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.


2 Answers

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.

like image 199
Vitus Avatar answered Oct 11 '22 19:10

Vitus


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:

  1. Either specify the type signature for the function (which you should anyways).
  2. Add the 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.

like image 33
krilitunris Avatar answered Oct 11 '22 19:10

krilitunris