For an assignment for FP we have to write a function that runs a state monadic computation given an initial state, and then returns the computed value and the number of operations counted.
Counts looks like this:
data Counts = Counts {
binds :: Int,
returns :: Int,
gets :: Int,
puts :: Int
} deriving (Eq, Show)
Where oneBind = Counts 1 0 0 0 (for example). There was also an mempty and <*> defined, but I wasn't able to use "mempty" instead of "Counts 0 0 0 0" with initCounts.
The States are defined as:
newtype State' s a = State' { runState' :: (s, Counts) -> (a, s, Counts) }
So far this is what I have got, but I've been stuck at about the same level for a few hours now.
run :: State' s a -> s -> (a, Counts)
run s ns = do
initState <- return ns
initCounts <- return (Counts 0 0 0 0)
newState <- return (runState' s (initState, initCounts))
newCounts <- return (runState' (retCounts newState) (newState, initCounts))
let st = let (a,_,_) = newState
in a
let count = let (c,_,_) = newCounts
in c
return (count)
retCounts :: State' s a -> State' s Counts
retCounts st = State' (\ (s, count) -> (calcCounts st, s, count))
calcCounts :: State' s a -> Counts
calcCounts st = undefined
I assume I have to use pattern matching in calcCounts to somehow actually count all the operators/functions, but right now I'm getting a type matching error:
Assignment4.hs:236:47:
Couldn't match expected type ‘State' (a, s, Counts) a0’
with actual type ‘(a, s, Counts)’
Relevant bindings include
newState :: (a, s, Counts) (bound at Assignment4.hs:235:5)
initState :: s (bound at Assignment4.hs:233:5)
ns :: s (bound at Assignment4.hs:232:7)
s :: State' s a (bound at Assignment4.hs:232:5)
run :: State' s a -> s -> (a, Counts)
(bound at Assignment4.hs:232:1)
In the first argument of ‘retCounts’, namely ‘newState’
In the first argument of ‘runState'’, namely ‘(retCounts newState)’
If I could get any help on how I could solve this type error and some hints to go from here, it would be highly appreciated.
PS: I realize it might be a good idea to rename calcCounts to something like calcFunctions
[EDIT: I'm also getting a different error when I work around this one by supplying a dummy value:
Assignment4.hs:233:5:
No instance for (Monad ((,) a)) arising from a do statement
In a stmt of a 'do' block: initState <- return ns
In the expression:
do { initState <- return ns;
initCounts <- return (Counts 0 0 0 0);
newState <- return (runState' s (initState, initCounts));
newCounts <- return (runState' retCounts (newState, initCounts));
.... }
In an equation for ‘run’:
run s ns
= do { initState <- return ns;
initCounts <- return (Counts 0 0 0 0);
newState <- return (runState' s (initState, initCounts));
.... }
]
There are a few problems here.
There was also an mempty and <*> defined...
Do you mean <> = mappend
? <*>
is the application operator for applicative functors.
run :: State' s a -> s -> (a, Counts)
run s ns = do
initState <- return ns
initCounts <- return (Counts 0 0 0 0)
newState <- return (runState' s (initState, initCounts))
newCounts <- return (runState' (retCounts newState) (newState, initCounts))
let st = let (a,_,_) = newState
in a
let count = let (c,_,_) = newCounts
in c
return (count)
Firstly, think about what the type of your do block is. It has the same type as run s ns
, which according to your type signature for run
is (a, Counts)
. As you know, do notation only works with monads. (a, Counts)
(or more accurately ((,) a)
) is not a monad, which is one of the reasons GHC is getting confused.
Next, you should note that in a do block:
initState <- return ns
is identical to
let initState = ns
If we rewrite run
to reflect this, we get the following:
run s ns = do
let initState = ns
initCounts = Counts 0 0 0 0
newState = runState' s (initState, initCounts)
newCounts = runState' (retCounts newState) (newState, initCounts)
let st = let (a,_,_) = newState
in a
let count = let (c,_,_) = newCounts
in c
return count
Now we can see the next big problem: you aren't actually binding any monadic variables in your do block! In fact, by manually using newState
etc you are circumventing the entire point of a state monad -- to keep track of state for us.
The fact is that all this is not so complicated as you might believe; we don't actually even need to use do notation. I advise that you think about this a little more before looking at the text below, to see if you can find the solution. As a hint: the function can be written simply in one line.
Here's how I might write run
:
run :: State' s a -> s -> (a, Counts)
run s ns = let (a, _, counts) = runState' s (ns, Counts 0 0 0 0) in (a, counts)