I want to make a funciton that adds 5 on odd calls and subtracts 6 on even calls using state monads.
f 5 = 10
f 7 = 1
f 4 = 9
f 2 = -4
0 is even so f 5 adds 5. 1 is odd so f 7 subtracts 6 and so on.
What I have now:
data Parity = Even | Odd deriving (Show, Eq)
not' :: Parity -> Parity
not' Even = Odd
not' Odd = Even
isOdd :: Int -> State Parity Int
isOdd x = state $ \(p) -> (if p == Odd then x + 5 else x - 6, not' p)
g n = do isOdd n
apply n = runState (g n) Even
I tried writing it like that, but everytime 'apply' is used the state is not saved. It only add 5 because of the even at the end. How do I make it save the state and only initalize it once and not every time ?
This answer I wrote a few days ago may be helpful.
Keeping it short, State s
is just a convenient way of simulating "stateful functions" f :: a -> b
as pure functions f :: (a,s) -> (b,s)
. To fit the monad framework though these are curried, so (roughly) of the form f :: a -> s -> (b,s)
.
The type State s b
is roughly s -> (b,s)
, which can be read as "a computation that returns a value b
and a final state s
and that requires an initial state s
to be run". A monadic function a -> State s b
is therefore a function that takes an input a
, and that can be run given an initial state s
, to produce a value b
and a final state s
.
Your function isOdd
is,
isOdd x :: Int -> State Parity Int
isOdd x = state $ \p -> (if p == Odd then x + 5 else x - 6, not' p)
which is roughly,
isOdd' x :: Int -> Parity -> (Int,Parity)
isOdd' x p = (if p == Odd then x + 5 else x - 6, not' p)
And your call,
apply n = runState (isOdd n) Even
is roughly,
apply' n = isOdd' x Even
That's all. You are essentially calculating
apply' n = --definition of apply'
isOdd' n Even
-- definition of isOdd'
(\x p -> (if p == Odd then x + 5 else x - 6, not' p)) n Even
-- application to the arguments `n` and `Even`
= (if Even == Odd then n + 5 else n - 6, not' Even)
-- simplifying
= (n - 6, Odd)
so,
apply' n = (n - 6, Odd)
Here is an example of how to sequence your function properly,
f :: Int -> State Parity Int
f n = isOdd n >>= (\x -> isOdd x)
or equivalently
f :: Int -> State Parity Int
f n = do x <- isOdd n
isOdd x
When you run it via e.g. apply n = runState (f n) Even
you are first running isOdd n Even
, to obtain a result m
and a new final state which will be False
, and then running isOdd m False
.