Reputation: 185
I'm trying to learn Haskell. I'm trying to write a programm that contains a "global state": Vars
. I want to change a component of the state (e.g. var1
) each time I call a function. The change can be a simple function on the components (e.g. +4). Also, it prints out the component changed. Here is what I've done so far (but I'm stuck). Edit: after running the code I want to see the recent version of the global state.
import Control.Monad.State
import Control.Monad.IO.Class (liftIO)
data Vars = Vars {
var1 :: Int,
var2 :: Float
} deriving (Show)
sample :: StateT Vars IO a
sample = do
a <- change
liftIO $ print a
-- I want to call change again and apply more change to the state
change :: StateT Vars IO a
change = do
dd <- get
-- I don't know what to do next!
main = do
runStateT sample (Vars 20 3)
evalStateT sample (Vars 20 3)
Upvotes: 3
Views: 504
Reputation: 9169
Let's try to solve your problem step-by-step starting from easier and small parts. It's important skill in programming and FP teaches you that skill in nice way. Also, working with State
monad and especially with several effects in monad-transformers helps you to reason about effects and understand things better.
You want to update var1
inside your immutable data type. This can be done only by creating new object. So let's write such function:
plusFour :: Vars -> Vars
plusFour (Vars v1 v2) = Vars (v1 + 4) v2
There exist ways in Haskell to write this function much shorter though less understandable, but we don't care about those things now.
Now you want to use this function inside State
monad to update immutable state and by this simulate mutability. What can be told about this function only by looking at its type signature: change :: StateT Vars IO a
? We can say that this function have several effects: it has access to Vars
state and it can do arbitrary IO
actions. Also this function returns value of type a
. Hmm, this last one is strange. What is a
? What this function should return? In imperative programming this function will have type void
or Unit
. It just do things, it doesn't return everything. Only updates context. So it's result type should be ()
. It can be different. For example we might want to return new Vars
after change. But this is generally bad approach in programming. It makes this function more complex.
After we understood what type function should have (try to always start with defining types) we can implement it. We want to change our state. There're functions that operates with stateful parts of our context. Basically, you interested in this one:
modify :: Monad m => (s -> s) -> StateT s m ()
modify
function takes function which updates state. After you run this function you can observe that state is modified according to passed function. Now change
can be written like this:
change :: StateT Vars IO ()
change = modify plusFour
You can implement modify
(and thus change
using only put
and get
functions which is nice exercise for beginner).
Let's now call change
function from some other function. What does calling mean in this case? It means that you execute monadic action change
. This action changes your context, you don't care about it's result because it's ()
. But if you run get
function (which binds whole state to variable) after change
you can observe new change. If you want to print only changed component, like var1
you can use gets
function. And, again, what type should sample
have? What should it return? If on the caller side you're interested only in resulting state, then, again, it should be ()
like this:
sample :: StateT Vars IO ()
sample = do
change
v1 <- gets var1
liftIO $ print v1
change
v1' <- gets var1
liftIO $ print v1' -- this should be v1 + 4
This should add you some understanding of what is happening. Monad transformers require some time to get used to them though it's a powerful tool (not perfect but extremely useful).
As as a side note I want to add that these function can be written much nicer using common Haskell design patterns. But you don't need to care about those right now, just try to understand what's going on here.
Upvotes: 2