State monad has been a point of interest for many functional programmers. While this is not a tutorial on how to design a state monad and it’s inside implementations, this blog gives you a general idea on what State monad is all about. You would probably get some sense of what is Monad too. Or may be you will get to use some Scalaz functions!
This small blog will find really useful for those who already know the programming language Scala. This isn’t anything new, but a collation of various ideas written by smarties in books such as “Functional Programming in Scala”, “Functional and Reactive Modelling” — Wow! Those books will change your life — trust me! Even if you are not functional in thinking these books will deal with your brain and improve your skills in any programming paradigm. The only reason for why I can’t find companies adopting FP quickly is because — its hard to get people who are patient enough to read through these type of resources and not because functional programming is not good for production. So lets make it a point that we read them.
An intentional Jargon for you!
Can you figure out anything? That’s just a function
S => (A, S) where S is a State (That is State in Simple English. Examples: The current State of your Bank Account, The current State of Random Number Generator Seed, The current State of You and so forth).
A is value that you extract out of the State. Ex:
A can be
Account Balance fetched from the current state of your
Bank Account. You get the point. Your next State clearly depends on the current State. This is a usual thing in any domain modeling (Sorry to use that jargon, those who don’t get it can discard that word)
So how do you keep track of the State? Oh before that, if you are still not convinced why do you need to keep track of the state, please pause here and convince yourself.
While you want to declare the handling of state transitions, it has to maintain referential transparency . Or to put in this way, you need to have a function that describes (not execute) how your state has to be changed. A pure description will be referentially transparent. I guess it doesn’t make much sense to you know. You will get there.
The answer to the above queries is State Monad. It is a fine level abstraction that does the plumbing work for you when it comes to traversing through various State changes. Example: The state transition of a bank account as a result of transactions.
You provide the pure function that defines how to move from one State to another. The actual plumbing of States through computations is taken care by the Monad and its combinators
Lets take an example:
You have a list of Bank Accounts. The bank needs to apply the transactions that happened over a day across all these bank accounts, changing each one of its account balance — We are modifying the state of bank accounts of everyone by updating its account balances, and obviously there will be multiple transactions for a single account. Let’s assume we have a map that represents the current state of all bank accounts.
Let’s use State monad to update
Balances — in FP language — let’s describe how the state of Balance should change given a list of
transactions. Remeber we are not passing the intial state yet. You can see that in the below example. We are only describing how the state should change given list of transactions. The current state of Balances is not passed in anywhere yet.
Monoid enables you to append two values based on its types without worrying about how they are going to be appended. Please note the Balance is a Double which has already got a monoid instance. In other words, given two double values,
Monoid[Double].append appends two doubles.
If you have a Monoid instance for
Balance you automatically get a Monoid instance of
Map[AccountNumber, Balance] . That means Compiler knows how to append two
maps as you expect it to do. Please get more details on Monoid in here.
So you now have a description of State transitions. As mentioned earlier you haven’t run this description (updatebalance function) by giving an actual initial state of
Balances. Don’t worry, that’s as easy as:
updateBalances(txns) run initialStateOfBalance
Don’t you think you got some idea?.. You may choose to stop here if you think you are overwhelmed, and for those who think this is something you knew before, please go and read the rest.
Let’s do more.
Let’s generate an account number using a Generator class and ensure that the account number doesn’t exist by querying a repository. This example is taken from Functional and Reactive Modeling. But I loved this example and I hope it is ok to paste some code which I wrote based on the book explanation.
But we love being declarative, so let’s do some fun stuffs. Let’s make the Generator a State. You can imagine the lifting function to be something like”
The above code is a replacement to imperative while loop. It doesn’t matter if you haven’t got this completely. The most important point here is that you’re still dealing with pure functions and their compositions.
This is an extract from the text book. Read through for further understanding!
- StateT is a monad transformer. It’s not important what it does here, except that the stateMonad function on StateT generates a State monad, with Generator as the state.
- The import in the second line makes available the various combinators within the State monad as well as the generic Monad abstraction. (Remember, State is also a monad.)
- modify is the combinator you used earlier that takes a function to modify the state. In our case, it’s creating another instance of Generator that will generate another account number.
- gets applies the transformation function passed to it to the value and returns the updated State monad. So here it checks whether the generated account number already exists. As long as exists returns true, you need to execute the body of whileM_.
- If you want to execute the computation and generate the next valid account number, you need to pass a start configuration of Generator and fire an exec on the monad.
The intention here is understanding State Monad and I hope it was useful!