Babra Cunningham
Babra Cunningham

Reputation: 2967

Data constructor error when implementing state monad?

I'm going through the state monad here and I'm trying to implement:

import Control.Monad.Reader
import Control.Monad.Writer
import Control.Monad.State

type Stack = [Int]

pop :: State Stack Int
pop = State $ (x : xs) -> (x, xs)

However I'm getting the following error:

"Data constructor not in scope:
    State :: ([t0] -> (t0, [t0])) -> State Stack Int
Perhaps you meant one of these:
    ‘StateT’ (imported from Control.Monad.State),
    variable ‘state’ (imported from Control.Monad.State)"

Am I missing something basic here?

Upvotes: 4

Views: 548

Answers (2)

Alec
Alec

Reputation: 32319

No you aren't. The tutorial is simplifying things a bit (or maybe it is just outdated - I don't go far back enough to know which of the two) is outdated. Control.Monad.State defines a monad transformer StateT. It also exports a simpler type synonym equivalent to what the tutorial is teaching you

type State s a = StateT s Identity a

However, that does mean that the constructor isn't State, it is StateT (and it has a generalized signature). Thankfully, you won't need to worry too much about that for now.

  • For constructing State, you can use the state function and pretend it has the signature state :: (s -> (a,s)) -> State s a (in reality, it has a more general signature - which you come across in error messages).
  • For de-constructing State, just use runState :: State s a -> s -> (a,s) instead of pattern matching.

From the example you gave:

import Control.Monad.Reader
import Control.Monad.Writer
import Control.Monad.State

type Stack = [Int]

pop :: State Stack Int
pop = state $ \(x : xs) -> (x, xs)

Upvotes: 9

amalloy
amalloy

Reputation: 92057

Why do you suppose that the interface to State a is through a data constructor that wraps a function s -> (a, s)? That is a simple way to implement State, but you are not provided that interface. Instead, use the constructs provided in Control.Monad.State.

One simple change is just to use the lower-case state function designed for this purpose:

pop :: State Stack Int
pop = state $ \(x : xs) -> (x, xs)

Alternatively, instead of working with this low-level view of State, you can work with it as a monad through its put and get functions:

pop :: State Stack Int
pop = do
  (x : xs) <- get
  put xs
  return x

Upvotes: 2

Related Questions