Understanding State with Elixir Agents
I mentioned a few blog posts back how managing state during the process of programming can be a very difficult task. Next generation programming languages are now starting to build in standardized approaches to solving this state change problem in a more elegant way. I've found myself doing more and more Elixir lately and I am sold on the concept of Agents. I get to write waaaay less code to take an object, or a piece of data through different states without the hassle of figuring out the best way to do it. This blog post will show a quick example of what I mean.
A most common example of something that changes state all the time is the bank account! Bank account state change can be for better or for worse. Modeling a Bank account in Elixir is a great way to show how simple this state management process is, and I will use the Agent module to illustrate this.
For a basic Bank account we are going to need 5 functions. These functions are
- open - Opens the bank account
- close - Closes Bank Account
- withdraw - Takes money from bank account
- deposit - Adds money to bank account
- balance - Gives current balance of bank account
In an effort to not get too complicated I'll start with these.
I like to always give my processes names because I don't like manually passing around a pid to all the functions I want to use. I am after all an automation engineer, so wherever I can find a shortcut I will!! Lets write the close function!
Its always necessary to have a way to gracefully stop a process and thats the whole point of the function above. In this case a user might need to close their account for whatever reason. This is a quick way to do it. Lets actually increase our net worth by building our deposit function.
I absolutely love function guards in Elixir. It saves me from having to worry about having too much logic in the function body that guards against bad parameters. In this case, we want to make sure the amount given is an integer. Its important to note as well, that it doesn't make a whole lot of sense to make a deposit less than $1, so its also smart to add another guard against bogus negative amounts being entered. In a perfect world we would be done! Our bank accounts would just increase in value over time given the way our BankAccount currently works, but unfortunately, we have to provide a way for users to take away from our account (BILLS, BILLS, BILLS). Let us painfully write this function next.
In my opinion this is a good use case for the if statement in Elixir. I am aware that you could do pattern matching to clean this function up, but I usually only resort to pattern matching when I'm expecting more than 2 possible decisions a program needs to make. In this case you either have enough money in the bank to make a withdrawal, or you don't. If you don't have money I want to raise an exception. This exception is important because if we ever wanted to add a supervisor for this BankAccount process, the reason for a sudden failure of the BankAccount would be made absolutely clear in the log output. Now we don't want to leave the balance of our account a mystery, so we need a way to display the balance. Lets finish up this module with the final function.
Bill Gates utilizing this module would look something like this....
The output should look like this below:
This is pretty cool because the state is tracked on its own by the agent. All you have to do is give it values. When I first started understanding the true reasons for Agents, I began to see so many use cases for them, especially for qa automation. State can now be managed independently of normal program flow which is HUGE! In languages that don't have state management carefully designed into them you would not be able to scale that program properly without major bugs and issues cropping up that you had no idea were present. Both Elixir & Clojure side step that nightmare by creating a standard way to handle state manipulation through Agents. This way, you don't have to worry about writing tons of code to track and manage state yourself, or exposing too many other objects with details of how and why to update a certain state. This is a huge win for programming, and actually makes it much more pleasurable than it used to be.