Refactor Stateful Code To Use A State Monad

Share this video with your friends

Social Share Links

Send Tweet
Published 6 years ago
Updated 5 years ago

When we start to accumulate functions that all work on a given datatype, we end up creating a bunch of boilerplate code in almost every function to handle the changes we need to make to our records’ values. This can lead to not only undesirable boilerplate present in all of our functions, but also can cause us to have to create variables just to manage our stateful changes.

We’ll take a look at a couple patterns that can act as early warning signs that will eventually cause us to not have a good time. Once we know what the smell is, we’ll look at how moving our computations into State can clean up all of our state management code by making it the responsibility of State. This allows our functions to only describe how state should change over time versus us having to change it ourselves.

Instructor: [00:00] One of the strengths of state is that it takes care of all our state management for us. This starts to show up in our code when we start to build collections of functions that work on a share date type to see how state could be used in situations like this. We'll start with the family of functions that work on a partial user record.

[00:17] Here, we have a function called update firstName which is defined as the curried function that takes the string followed by a user record, and gives us back a user record. We pass to curry a function that takes a first name and returns a partially applied assign function. Ready to take a user record which it'll merge with our object to first name.

[00:38] To get a feel of how we would work with a function like this, we can log out the result of calling update firstName, passing in Jimmy, followed by our user record and saving it down. Looks like we've updated our first name as expected, but we have a problem Mr. Lawrence, full name was not updated with Jimmy.

[00:57] We can read it at by creating a function will call build fullName, which will take a user record that we name user. As we need both the first and last names, we'll plug them off our user record using a bit of destructuring, like so. Declare another string that matches our key of full name in our user record that will hold the result of calling joinWords that will join our two names with the space.

[01:23] Now with our full name all built out in our desired format we just return the result of calling update fullName passing in our new string followed by our user record. By the by, we define build fullName as a unary function that takes a user record giving us back a new user record with our full name modified.

[01:43] We now need to make sure that this modification happens every time we update our first name. To do this, we'll extend update firstName with some function composition using the compose helper function. We need this to happen after our first name assignment, so we drop it in as the first argument to compose.

[02:02] Give it a mighty little save. Now, we see that we get back our Jimmy Pickles as the full name. Taking a quick tour of the functions in this file, a common pattern starts to rear its ugly little head. Every function here requires and returns a user record. Putting the responsibility of managing our record on the shoulders of our functions, the implications of this start to become clear in our new build fullName function.

[02:28] It can cause us to have to needlessly create variables just to work on portions of our user. It also locks us in to having to hold on to our user here as we needed later to merge the result of our computation into the original value. Over time as this file grows, very simple functions will become tainted with this filth and will result in hard to manage code.

[02:51] Let's take a look at how state can help to clean up this mess. We have an analog to the original update firstName function taking in a string. Using modify, we kick back a state instance that will use assign to merge our new name into the state. We need to update our edge call to accommodate our new function.

[03:10] We'll remove user from this call as it is no longer needed. Then, we just point our required to the model file to pull it in, seeing we get back our lazy old state. As we only care adopt the state portion, we'll run our instance with [inaudible] , providing our user record setting it down to see we've updated it Jimmy, and like before Jimmy does not appear in our full name.

[03:33] Now, let's assemble our state base version of build fullName which will define as a state transition going from unit to a new state user of unit. We can implement by starting with a function that takes nothing as the user is in the state. We now need to access our first and last names to join them for our full name, like we did in our old function.

[03:54] We have some accessors already defined that use get to plug their respective values plopping them into the resultant. Because we need both of them to be applied to our joinWords function, we reach for the lift A to helper as a means to lift our joinWords function and apply to it, the result of calling our two name accessors.

[04:14] First name first followed by the last. This will return us a new state instance with our full name in the resultant, but we're not quite finished. We need to take this string and use it to update our user with our new full name. As we have an update full name transition already defined in our file, we just chain it in to complete this stateful transaction.

[04:35] Extending our name update is both simple and clean. We just meander up to our getFirst name function and chain in our shiny new stateful transaction, describing what we want done to the state without having to do it ourselves. We save it down again and find that much to our delight full name now reflects the name of our good friend Mr. Jimmy Pickles.

Jorge Cortes
Jorge Cortes
~ 6 years ago

Hi everyone, I've been watching these lessons which I think are one of my favorites :) But I have a question on this video. When I get to the part where I'm using propOf the node throws an error that says:

Error: Cannot find module 'crocks/helpers/propOr'

How can I fix that? thank you1

Ian Hofmann-Hicks
Ian Hofmann-Hicksinstructor
~ 6 years ago

Sorry for the trouble, propOr is not available in crocks yet. (It will be in the next release tho) This lesson is using a propOr in the helpers.js file.

it can be found on the github here.

hope this helps!!

Jorge Cortes
Jorge Cortes
~ 6 years ago

ohhh thanks a lot !!

Mitchell Stewart
Mitchell Stewart
~ 6 years ago

Thanks! This was one of my favourite egghead courses.

Robert Pearce
Robert Pearce
~ 6 years ago

Great that this mentions recognizing not great patterns and uses what we've learned to address the issue.

Markdown supported.
Become a member to join the discussionEnroll Today