Let's build a compiler (in Haskell): Part 11 - Introducing the State Monad
posted by Geoff Ford on 19 Oct 2010
In the last installment you may have noticed that there was a lot of state threading in the emitter functions. This involved a lot of careful ordering of s1, s2 etc. which is quickly becoming a problem. Especially so when you aren't consistent with parameter ordering - getLabel took the counter last whilst emitStatment was taking the counter as the first parameter.
It is a misnomer that functional programming cannot have state, rather it cannot have side effects, which is a very different ideal. If you need to maintain state in your application, and a compiler inherently does, then you need to continually pass this state from one function to the next. Conversely, all your functions that modify the state must all return the entire state.
The State Monad was created for instances exactly like this. It defines an explicit interface for passing state from one function to the next, and provides several functions for executing the functions using this state. I'm not going to provide yet another State Monad breakdown - for that I can recommend the following: the official wiki, a great explaination of the mechanics and most recently Learn you a Haskell.
Some things I learned the hard way
The State Monad and Record Syntax
I really struggled with using the State Monad in a situation where multiple peices of state are used. As the compiler is going to need many peices of state such as the label counter, break label, symbol table etc., I started looking for info on record syntax and State Monads. The best I could find was a few paragraphs in Real World Haskell which was pretty clear if brief.
Types for Functions that use the State Monad
Almost all the sample code and tutorials on using the state monad involved no parameters for the functions. All the types where of State s a and almost no examples showed how to pass in extra parameters. Thankfully Learn you a Haskell had some examples that used b -> State s a which lead me to figure out that functions using the State monad can have any type but final type of a function must be State s a and that the type of the function used with runState et.al. must be State s a only. Took me a while to figure this one out.
Random number examples
Annoyingly, 9 times out of 10, any example on the State Monad uses the random number example. This example is next to useless as it neatly ties up the original state in StdGen which hides the actual usage of State. Please try to come up with something original and worthwhile that explains the whole thing. Thanks to Learn you a Haskell, which uses a simple stack example, I was able to figure how and when the State is called.
Simple Example
Rather than try to retro fit the State Monad throughout our emitter in this article, which will be complex and tedious, I will use a simplified example.
First thing we do is create a data type to collect all out stateful information, and a new type using State and our new data type.
-- This will hold all of the data in our state
data EmitStuff = EmitStuff {
lblCounter :: Int,
lastLabel :: String
} deriving (Show)
-- It is common practice to make your own state type
type MyStateMonad = State EmitStuff
lblCounter will hold the current count for creating numbered labels, whilst lastLabel will be the last generated label.
Next we will create a function that generates a new label. This will solely use the state to calculate the new label, and then update the state. Do notation makes this much clearer to read.
-- Generates a new label based on the current label counter
-- Stores the generated label, and updates the label counter
newLabel :: MyStateMonad String
newLabel = do
st <- get
let l = "L" ++ show(lblCounter st)
put st { lblCounter = lblCounter st + 1, lastLabel = l}
return l
Next we'll create an example that takes an extra parameter. A function that generates a label with a comment.
-- Emits a label followed by a comment
emitLabelAndComment :: String -> MyStateMonad String
emitLabelAndComment x = do
l <- newLabel
return (l ++ ": #" ++ x)
Let's take a moment to understand what this one is doing. You'll recall that the State Monad is a wrapper for the type s -> (a, s) which means that the above type signature expands into String -> s -> (String, s). This means that this function will also take the state as input, which is then passed onto newLabel through the State Monads internals. This is a key concept in turning your manual state management code into something that can be used with the state monad. Mike Vanier explains how we can turn the 5 different type of functions using state into one standard format: s -> (String, s).
The next example is calling multiple functions that use state from one function.
-- Simple example showing how we no longer need to manually thread the state when creating labels
fakeIfStatement :: MyStateMonad String
fakeIfStatement = do
falseBranch <- newLabel
endLabel <- newLabel
return ("if not true then \njump " ++ falseBranch ++ "\nDo some true stuff\njump " ++ endLabel ++ "\n" ++ falseBranch ++ ": #some stuff to do if false\n" ++ endLabel)
Executing the State Monad
Finally we need to tie it all together. The state monad comes with three utility functions for executing the code.
runStatereturns the result and the state as a tupleevalStatereturns the result and discards the state i.e.(result, _) = runStateexecStatereturns the state only and discards the result i.e.(_, state) = runState
Each of these functions take a fucntion of type State s a and an intial state of type s. So first we will create our initial state.
initialState = EmitStuff { lblCounter = 0, lastLabel = "" }
We can then use any of the utility functions to execute our code against this state.
runState fakeIfStatement initialState -- or evalState newLabel initialState
But this is not very useful as the state is reset everytime we apply it to a new function. We have already seen the answer in fakeIfStatement. We simply create one function that executes all the other functions for the duration of the state.
-- Tying it all together
emitAll = evalState emit EmitStuff { lblCounter = 0, lastLabel = "" }
where emit = do
a <- emitLabelAndComment "This is the first label"
b <- fakeIfStatement
c <- emitLabelAndComment "This is the last label"
return (a ++ "\n" ++ b ++ "\n" ++ c)
Now if you were to execute putStrLn & emitAll in ghci you will see labels nicely placed and incremented as expected.
You can download and play with all of the above from Github.
Implementing in LBaCH
Implementation into the existing Emitter.Control required a rather large rewrite (94% according to git), but was not terribly difficult. It now reads much clearer and looks a lot more like the BNF notation.
For now I have made the state entry point at the emitBlock level, but in future it may be moved up to encompass the entire emit process.
emitBlock :: Block -> String
emitBlock b = evalState e EmitterData { lblCounter = 0, lastLabel = "" }
where e = do
a <- emitBlock' b
return a
You can find all the gory details here.
by BrandStar Entertainment - March 7, 2012 11:02 a.m.
This means that this function will also take the state as input, which is then passed onto newLabel through the State Monads internals.
by BrandStar Entertainment - March 7, 2012 11:02 a.m.
This means that this function will also take the state as input, which is then passed onto newLabel through the State Monads internals.
by link building - May 20, 2012 8:48 p.m.
"Dofollow" is the natural way of linking. I actually still don't get exactly why numerous blog managers use nofollow option for blog responses. I am a little frustrated because I am starting a blog commenting company and I find quite hard to get great quality dofollow blogs.
by backpacking tents - May 29, 2012 12:05 p.m.
Posts like this brgihten up my day. Thanks for taking the time.
by cheap car insurance quotes - June 25, 2012 5:39 a.m.
I was seeking for a great place to get information about comment luv but i think finally i am at the right place so going to take some advantage of your quality writing and nice information .
by cheapest car insurance quotes - June 25, 2012 5:40 a.m.
I have read your all of the provided information and at the end i agree with your opinion but can you let me now that you offer the cheapest health insurance in the market ?
by Cheap Health Insurance - June 25, 2012 5:42 a.m.
I was seeking for a great place to get my insurance at the affordable rates but i think finally i am at the right place so going to take some advantage of your quality writing and nice information about insurance
by Cheap Life Insurance - June 25, 2012 5:42 a.m.
I have read your all of the provided information and at the end i agree with your opinion but How and where can i get the affordable life insurance ?
by Cheap Taxi Insurance - June 25, 2012 5:43 a.m.
I have read your all of the provided information and at the end i agree with your opinion but How and where can i get the Cheapest Auto Insurance?
by cheap office insurance - June 25, 2012 5:44 a.m.
I was seeking for a great place to get information about comment luv but i think finally i am at the right place so going to take some advantage of your quality writing and nice information .
by cheap home insurance - June 25, 2012 5:44 a.m.
You should keep an eye out for more details on this exciting program that will bridge the gap to your highest potential and utmost satisfaction.
by cheap van insurance - June 25, 2012 5:44 a.m.
Think we need to bring more ideas for this purpose. Involvement of young people can be handy in this regard. I am happy to find a good post here. Thank you.
by cheap 4x4 insurance - June 25, 2012 5:45 a.m.
You should keep an eye out for more details on this exciting program that will bridge the gap to your highest potential and utmost satisfaction.
by cheap boat insurance - June 25, 2012 5:45 a.m.
Thanks a lot for such a wonderful post, the stuff posted were really interesting and useful. The quality of the content was good and clear. Thanks for the post
by cheap dog insurance - June 25, 2012 5:46 a.m.
This one is very nicely written and it contains many useful facts. I am happy to find your distinguished way of writing the post. Now you make it easy for me to understand and implement. Thanks for sharing with us.
by cheap bike insurance - June 25, 2012 5:46 a.m.
it’s really a good write-up, would be interested to read such more informative draft from you ahead, can be a wonderful help to the new readers like me.
by Chrysler Fifth Avenue Power Steering Rack - July 11, 2012 9:27 a.m.
Hello! I just would like to give a huge thumbs up for the great info you have here on this post. I will be coming back to your blog for more soon.
by Shirley Morales - July 12, 2012 7:46 a.m.
You write something that people could understand and make the subject intriguing for everyone. I will save this for future use.
by Auto abbreviations - Sept. 22, 2012 10:58 a.m.
To understand how a clutch works, it helps to know a little bit about friction, which is a measure of how hard it is to slide one object over another. Friction is caused by the peaks and valleys that are part of every surface -- even very smooth surfaces still have microscopic peaks and valleys. The larger these peaks and valleys are, the harder it is to slide the object. You can learn more about friction in
by Giam gia - Nov. 28, 2012 8:57 a.m.
by OBD2 Scanner - Jan. 14, 2013 2:37 a.m.
Expect you to have a better article to share.Thanks for all your efforts that you have put in this. Thanks for sharing such a wonderful article. AUTO
by Essay help - April 3, 2013 1:28 p.m.
This a comprehensive coverage and a solid tutorial. Thank you for sharing this.
by Essay help - April 3, 2013 1:30 p.m.
First I had some difficulty executing the state monad but thanx for the great help. This a comprehensive coverage and a solid tutorial. Thank you for sharing this.
by Acompanhantes Brasilia - April 3, 2013 8:31 p.m.
Nice article
by vending machines los angeles - April 14, 2013 10:35 a.m.
I am visiting this site first time & it has awesome information. I have come to know a lot of information after visiting this site. Keep this sort of posting in future as well. I loved reading it and will surely suggest everyone to read it.
by Toyota Aristo Vertex - May 17, 2013 5:53 a.m.
Some things in here I have not thought about before.Thanks for making such a cool post which is really very well written.will be referring a lot of friends about this.
by roksport - May 17, 2013 10:47 a.m.
nice read, diolch!
by GTA 5 Free Beta - May 22, 2013 9:40 p.m.
great post GTA 5 Free Beta
by GTA 5 Free Beta - May 22, 2013 9:40 p.m.
great post GTA 5 Free Beta
by GTA V Beta - May 22, 2013 9:41 p.m.
GTA V Beta
by GTA V Free Beta - May 22, 2013 9:41 p.m.
GTA V Free Beta
by GTA 5 Beta - May 22, 2013 9:42 p.m.
GTA 5 Beta
by gta 5 download - May 22, 2013 9:42 p.m.
gta 5 download
by EQUITY TIPS - June 1, 2013 6:43 a.m.
very nice post , ilke your way of shareing the post .very usefull infomation in this post you cover all meaningfull points about stocks. really thanks for this posting.
by gta 5 download - June 3, 2013 12:06 a.m.
gta 5 download
by mobiles price in India - June 7, 2013 8:34 p.m.
The post is nicely written and it contains many good things for me. I am glad to find your impressive way of writing the post. Now it's become easy for me to understand and implement the concept. Thanks for sharing the post. <a href="http://www.bestpriceindia.co.in/">mobiles price in India</a>
by acne treatment reviews - June 8, 2013 11:06 a.m.
nice post
by acne treatment reviews - June 8, 2013 11:07 a.m.
nice post
by Android Helova - June 8, 2013 4:30 p.m.
nice article
by Android Helova - June 8, 2013 4:31 p.m.
nice article
by Android Helova - June 8, 2013 4:31 p.m.
nice article
by Android Helova - June 8, 2013 4:32 p.m.
nice article but i dont really undestand that code
by Kamruzzaman - June 17, 2013 7:05 a.m.
This is a very interesting article.Thank you for this case. <a href="http://webpagedesigncity.com/" title=" best web design "> best web design </a>