1

I am having a hard time understanding this tutorial: https://acm.wustl.edu/functional/state-monad.php

I am creating my own function that reverses a list and returns a State with the lowest element and the reverse of the list. I am very new to Haskell as well. Here is my code:

myFunct :: Ord a => [a] -> State a [a]
myFunct t = do
        let s = reverse t
        let a = minimum t
        return s a

I can't find an other material on this either. This is the error I am getting.

 Couldn't match type ‘[a]’
                 with ‘StateT a Data.Functor.Identity.Identity [a]’
  Expected type: a -> State a [a]
    Actual type: a -> [a]
• The function ‘return’ is applied to two arguments,
  its type is ‘a0 -> m0 a0’,
  it is specialized to ‘[a] -> a -> [a]’
  In a stmt of a 'do' block: return s a
  In the expression:
    do let s = reverse t
       let a = minimum t
       return s a
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • Do you have an actual question though? – user253751 Mar 08 '18 at 03:25
  • What is your question actually? – wisn Mar 08 '18 at 03:30
  • Why doesn't my code compile. I will post the error. –  Mar 08 '18 at 03:31
  • Also, show us how you pass the argument (value) to the function. – wisn Mar 08 '18 at 03:35
  • 1
    `return` takes one argument. – user253751 Mar 08 '18 at 03:38
  • So what do you suggest "returning " ? –  Mar 08 '18 at 03:39
  • How about `return (s a)`? – wisn Mar 08 '18 at 03:42
  • That does not work either. I got -- Couldn't match expected type ‘a -> [a]’ with actual type ‘[a]’ –  Mar 08 '18 at 03:43
  • I am thinking some kind of helper function is needed but I am at a mental block right now. –  Mar 08 '18 at 03:43
  • Then, try `return (s [a])`. – wisn Mar 08 '18 at 03:44
  • I think it should be `return (a s)`. – wisn Mar 08 '18 at 03:45
  • None of those work lol similar error –  Mar 08 '18 at 03:47
  • 2
    @WisnuAdiNurcahyo These won't work. Neither `s` nor `a` are functions. The OP is probably looking for, among other things, the `set` function. – duplode Mar 08 '18 at 03:50
  • please tell me more about this "set" you speak of –  Mar 08 '18 at 03:51
  • @duploade Okay. – wisn Mar 08 '18 at 03:56
  • @loneWolf is your State type like this? `newtype State s a = State { runState :: (s -> (a,s)) }` – wisn Mar 08 '18 at 03:57
  • Yes, what ever the "default" is, that is what I use –  Mar 08 '18 at 03:59
  • 1
    @loneWolf There was a typo in my comment above: I meant `put`, not `set`. `put` is a straightforward way to change the state when working with the `State` monad. See, for instance, [*State Monad and 'put' function in Haskell*](https://stackoverflow.com/q/36585863/2751851) – duplode Mar 08 '18 at 03:59
  • "I can't find an other material on this either" -- [*Haskell State Monad*](https://stackoverflow.com/q/33194267/2751851); [*Why must we use state monad instead of passing state directly?*](https://stackoverflow.com/q/31475770/2751851); https://en.wikibooks.org/wiki/Haskell/Understanding_monads/State ; http://learnyouahaskell.com/for-a-few-monads-more#state – duplode Mar 08 '18 at 04:05
  • Another nice tutorial for the State Monad is [https://wiki.haskell.org/Simple_StateT_use](https://wiki.haskell.org/Simple_StateT_use) – Jogger Mar 10 '18 at 16:19

2 Answers2

2

Since you're using a do block, I assume that you want to use State as the Monad it is. That's fine, but I'd suggest, then, to make the list of values ([a]) the state, and the single, minimum value the 'return value'.

This means that you can simplify the type of your function to myFunct :: Ord a => State [a] a. [a] is the type of the state, and a is the type of the return value.

Notice that there's no explicit 'input value'. Inside the State monad, the state is an implicit context that's always there.

You can now rewrite the computation like this:

myFunct :: Ord a => State [a] a
myFunct = do
  t <- get
  let s = reverse t
  put s
  let a = minimum t
  return a

You can write the computation more succinctly, but I chose to write it out explicitly to make it clearer what's going on. get retrieves the current value of the implicit state, and put overwrites the state. See the documentation for more details.

You can run it like this:

*Q49164810> runState myFunct [42, 1337]
(42,[1337,42])
*Q49164810> runState myFunct [42, 1337, 0]
(0,[0,1337,42])
*Q49164810> evalState myFunct [42, 1337, 0]
0
*Q49164810> execState myFunct [42, 1337, 0]
[0,1337,42]

runState takes an initial state, runs the myFunct computation, and returns both return value and final state. evalState works the same way, but returns only the return value, while exacState only returns the final state.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
2

You're in luck: State is the easiest monad to understand.

Please do not get discouraged by the fact that your function does not need State at all, insofar as you use reverse and minimum from the standard library.

myFunct' :: Ord a => [a] -> ([a], a)
myFunct' xs = (reverse xs, minimum xs)

(It would run like this:)

λ myFunct' [1,2,3]
([3,2,1],1)

Notice though that, in order for you to apply both reverse and minimum to a list, you will need to traverse it two times. This is when State may get handy: using it, you can only traverse the list once, thus, hopefully, gaining some speedup. Read on to find out how.

So, State is a function of a special kind: the thing you give it (also called "state") is kept in a magic box where you can observe it, or replace it with another thing of the same type at any time. If you have experience with imperative languages, you may find it easy to think of State as an imperative procedure and "state" as a local variable. Let us review the tools that you may use to construct and execute a State:

  • You may observe the thing in the box with the (inappropriately named) function get. Notice that this does not change the state in any way − what you obtain is merely an immutable copy of its current value; the thing stays in the box.

    You would usually associate your observation with a name, then use it as an ordinary value − for example, pass to a pure function:

    stateExample1 :: State Integer Integer
    stateExample1 = do
        x <- get  -- This is where we observe state and associate it with the name "x".
        return $ x * 2  -- (* 2) is an example of a pure function.
    

     

    λ runState stateExample1 10
    (20,10)  -- The first is the return value, the second is the (unchanged) state.
    
  • You may replace the thing in the box with another suitably typed thing; use the function put:

    stateExample2 :: State Integer Integer
    stateExample2 = do
        x <- get
        put $ x * 2  -- You may think of it as though it were "x = x * 2" 
                     -- in an imperative language.
        return x
    

     

    λ runState stateExample2 10
    (10,20)  -- Now we have changed the state, and return its initial value for reference.
    

    Notice that, though we changed the state, our observation of it (that we named "x") still has the same value.

  • You may run the State function, giving it an argument (we'd call it "initial state"):

    y = runState stateExample1 10
    

    It is the same as:

    y = stateExample1(10);
    

    − in an imperative language with C-like syntax, except that you obtain both the return value and the final state.

Armed with this knowledge, we can now rewrite your proposed myFunct like this:

myFunct :: Ord a => [a] -> State (Maybe a) [a]
myFunct [ ] = return [ ]
myFunct t = do
        let s = reverse t
        let a = minimum t
        put (Just a)
        return s

 

λ runState (myFunct [1,2,3]) (Just (-100))
([3,2,1],Just 1)
λ runState (myFunct []) (Just (-100))
([],Just (-100))

If we regard State as an imperative procedure, then the reversed list is what it returns, while the minimum of the list is what its final state would be. As the list may be empty, we have provisioned an optional default value for the minimum. This makes the function total, which is considered good Haskell style:

λ myFunct' []
([],*** Exception: Prelude.minimum: empty list
λ runState (myFunct []) Nothing
([],Nothing)

 

Now, let us reap the benefit of State by writing a function that returns both the minimum and the reverse of a list in one pass:

reverseAndMinimum :: Ord a => [a] -> ([a], Maybe a)
reverseAndMinimum xs = runState (reverseAndMinimum' xs [ ]) Nothing

reverseAndMinimum' :: Ord a => [a] -> [a] -> State (Maybe a) [a]
reverseAndMinimum' [ ] res = return res
reverseAndMinimum' (x:xs) res = do
        smallestSoFar <- get
        case smallestSoFar of
            Nothing -> put $ Just x
            Just y  -> when (x < y) (put $ Just x)
        reverseAndMinimum' xs (x: res)
  • First off, this is an iterative algorithm that thus needs a starting value for the minimum. We hide this fact in reverseAndMinimum', supplying Nothing for the starting value.

  • The logic of the reverse part I borrowed from the modern Prelude.reverse. We simply move elements from the first argument xs to the second argument res, until xs is empty.

  • This is the part that finds the smaller of the current x and the value stored in the state box. I hope you'll find it readable.

        case smallestSoFar of
            Nothing -> put $ Just x
            Just y  -> when (x < y) (put $ Just x)
    
  • This is the part that does the recursion:

        reverseAndMinimum' xs (x: res)
    

    It applies reverseAndMinimum' again, but to a strictly smaller list xs; the monadic wiring automagically transfers the box with the current minimum down the line.

Let us trace the execution of a call to reverseAndMinimum'. Suppose we say:

runState (reverseAndMinimum' [1,2,3] [ ]) Nothing

What will happen?

  1. The smaller of 1 and Nothing is 1. So, the Nothing in the box will be replaced by Just 1.
  2. The State will be called again, as though we called it with a code like this:

    runState (reverseAndMinimum' [2,3] [1]) (Just 1)
    

And so on, until the parameter becomes an empty list, by which time the box will surely contain the smallest number.

This version actually performs faster than myFunct' by about 22%, and uses somewhat less memory as well. (Though, as you may check in edit history, it took some effort to get to it.)

That's it. I hope it helps!

Special thanks to Li-Yao Xia who helped me devise the code for reverseAndMinimum that actually beats myFunct'.

Ignat Insarov
  • 4,660
  • 18
  • 37
  • Thank you for taking the time to write this. I appreciate it a lot. It really did help. –  Mar 08 '18 at 15:54