简体   繁体   中英

Haskell State as function type

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

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.

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' .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM