I want to write a function for calculating the average using the State Monad in haskell this is the code I wrote as far
import Control.Monad.State
type MyState = (Double,Double)
media s (a,n)= ((a*n+s)/(n+1),n+1)
getAverage:: Double ->State MyState s1-> Double
getAverage s c=get >>= \s0 -> let (x,s1) =media s s0
in put s1 >> return x
I got this error when compile in GHCI, and I stuck there can you help me to understand what is wrong, thank you in advance
Note: camccann's answer is better than mine, but mine takes a slightly different approach and gives an example of how to evaluate the state monad, so I'm leaving it here for reference.
We can start trying to figure out the problem by removing the type signature for
getAverage
and the argument (c
) that doesn't appear in the function:This still doesn't compile, because we're trying to
put
something that doesn't have the right type:s1
is aDouble
, not aMyState
. This is easily fixable:We could also leave the
let
pattern unchanged and just sayput (x,s1)
: I'm doing it this way instead so that ours1
has the same type ass0
.This compiles, so now we can fix the type signature. If we ask GHCi for the type, it returns the following:
Double
is an instance ofFractional
, andState MyState
is an instance ofMonadState (Double, Double)
, so we can use something very similar to your original type forgetAverage
:This function doesn't really "get" the average: it updates it after adding a new value, so let's rename it appropriately:
Now we can define a
getAverages
function that takes a list ofDouble
s, runs them throughupdateAverage
, and returns a list of the intermediate averages at each step:This does what we'd expect:
Note that to do anything useful with the
State
monad you'll always have to useevalState
(or the closely relatedrunState
andexecState
).The code you provided gives this error:
All this means is that the type resulting from the expression ("inferred") disagrees with the type signature ("expected"). In this case,
getAverage
operates in theState
monad, so it's the type signature that's incorrect, as it can't evaluate to a non-monadic type.Your code has other problems besides that, however, and won't compile even after fixing that particular issue. First a few stylistic issues to make it more readable:
getAverage
has an unused parameter, which is supposedly a value in theState
monad, which doesn't really make sense anyway.do
notation is usually clearer than using(>>=)
and lambdas, especially for something likeState
.in
goes with thelet
that's inside the lambda.Making those changes we have this:
...which makes it easier to spot the next error: The second argument of
media
is a 2-tuple, ands1
is just a single number, but you're trying to use both for the state value. Probably what you wanted was to set the state to(x, s1)
, but return onlyx
.This compiles just fine, but still needs some tidying:
media
needs to update the entire state value, so rather thanget
ting andput
ting, just use themodify
function.fmap
ingfst
overget
is more straightforward.So now we have something like this:
We can also note that
getAverage
is kind of doing two different things, and split it into separate functions:Edit: And since I forgot about the minor detail of actually getting the results back out of the monad, replacing
updateAverage
withgetAverage
in Travis Brown'sgetAverages
function will let it work on my code above.