Map And Evaluate State With A Stateful Monad

Share this video with your friends

Social Share Links

Send Tweet
Published 6 years ago
Updated 5 years ago

We explore our first stateful transaction, by devising a means to echo our state value into the resultant for independent modification. With our state value in the resultant, we explore using map to lift functions into our type as a means to modify the resultant.

In our exploration we find that not only can we change values of the resultant, but we can change the type as needed. We also find that we can combine subsequent map calls into function compositions, allowing us to clean up our code and combine our mappings to avoid excessive interactions with our State datatype.

To wrap it all up, we take a look at a handy State construction helper called get that we can use to query the state into resultant. This allows to read from our state for computations that are based on the value of a given state.

Instructor: [00:00] We'll start off by bringing in a couple constructors. First, we have the star of the show state. We also need a product type called pair that will be needed for the states construction. We define a state as a product type with the fixed type state s on the left and a variable type resultant on the right.

[00:18] When constructing a state, we need to provide it a function that will take a fixed state and return as a pair with the resultant as the first, and the state as the second. We have the state instance that will yield us a pair which adds 10 to the state as the resultant in the first, and provides an unmodified state s in the second.

[00:38] Let's log this out and get a gander at what we're working with by passing our state into this log function. We now need to provide some initial state which we'll do by passing 23 to this RunWith method, which as we see gets us our pair 33 23. We can extract our resultant from the pair by calling fst on it, and s and d will get us our state.

[01:01] Looking at this code, it seems rather cumbersome to enlist this construction every time just to modify the resultant. A better interface would allow us to eject or lift any JavaScript function, and let the type do all the plumbing for us. An interface like this does exist in the forum of a method name map.

[01:21] States map is defined as a method on a state of sa that takes a function a to b, and gives us back a new state of sb. In order to use map, some adjustment will be needed. We need a construction that will put the current state into the resultant on the right, so we can map over it. For this purpose, let's create a function which will call getState.

[01:43] GetState will provide us with a state instance with s on the left and right, which will abbreviate as just s. Next step, we make it a function that accepts nothing or a unit and return us our state. We implement the function to return our construction, update the name, and just remove the plus 10, which in turn provides us a pair with the state branched on both sides, just what we were looking for.

[02:12] Now, let's update our call downstairs to get it's instance from our new helper by giving it a call passing and nothing. We'll give it a save and we see that our state is in the second. Using fst to check the resultant, we see it is there as well. Finally, we see a pair of 23 23 for the type as a whole. Perfect.

[02:33] Now, let's set about the task of making the function we want to lift in. To keep it co-insistent, let's use this curried add function that takes a number and another number returning us a number. We give it in X, then a Y, and it gives us the result of those numbers under addition. All that is needed now is to lift our function into our state.

[02:53] We just call map on our instance, partially applying 10 to add, and to make our linter happy, we should probably bring in the add function by destructuring it off of the helper's object provided by the helper's pile which we can require in.

[03:08] When we give it a save, we see we now have 33 in the resultant. Let's have a little fun and change the state to zero, which will change our resultant to 10 while leading our original state of zero intact. When map is defined earlier, it was mentioned that it takes a function from a to b. In our case, we provided a function from number to number or a to a.

[03:31] The beauty of the resultant is it is not constrained to a type like the state portion is. We can vary the type of the resultant to accommodate our needs. To demonstrate this, let's pull in this pluralize function. It is defined as a function that takes a Tuple string string as a context, and takes a number as it's data returning us a string.

[03:51] The strings represent the singular and plural forms while the number is the value to be pluralized. Now is the time to get awesome. Let's replace this siggy with a function called makeAwesome. We can use pluralize to move our number A into a string B.

[04:07] MakeAwesome is defined as a function that takes a number and returns us a string. To implement, we just call pluralize partially applying awesome for the singular and awesomes for the plural, leaving us a function that matches our definition. Just like with add, we need to a piece or linter by plucking pluralize off of the helper's to make it available in our file.

[04:31] With pluralize now in scope, let's replace our add function with a call to makeAwesome and check the state. We verified that it's still zero, but now when we pull the resultant, we see we have zero awesomes. Passing one gets us one awesome, while one hundred results in a hundred awesomes which is not too shabby, if I do say so myself, but it doesn't stop there.

[04:54] Just by inserting our add function, we can map over both functions resulting in a 110 awesomes. If we narrow our eyes just a wee bit, we may notice that these calls resemble function composition. We can put this theory to the test by combining these functions using a compose function.

[05:12] First, let's declare a function called flow which is defined as a function that takes a number and returns back a string. We implement it by using composed constructor function that calls makeAwesome passing in the result of adding 10 to flows input. If our theory is correct, then flow must be equivalent to these two mappings.

[05:33] Before we can test our theory, we need to pull in the compose helper function from crocks and make sure it's in scope for a new function. With everything in its place, let's jump down to the bottom and replace our two maps with one map over our new flow.

[05:48] We'll give it a save to get a 110 awesomes as the resultant, and the state comes back as the number 100 seems equivalent indeed. Supplying -9 is the state. We see that our state portion now holds -9, well -9 plus 10 gives us one whole awesome in our resultant.

[06:09] Finally, we see that our original state of 23, now yields 33 awesomes. Now to wrap this up, lifting different functions in the state to map over the resultant is so common place. That state provides a construction helper on the constructor named get which will destructure off of state, removing the need to build this construction ourselves.

[06:31] When our getState call is replaced with the call to get, we see the same 33 awesomes in the resultant, and the expected 23 in the state. As a whole, we get a pair 33 awesomes 23.

Briisk Sp. z o.o.
Briisk Sp. z o.o.
~ 6 years ago

Why is it that the State has a signature State s a and the function that the constructor takes returns a pair in the opposite order s -> (a, s) ?

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

@Briisk Very keen observation. The answer is hard to explain as I do not know your understanding of Category Theory. The short version is:

The State Monad is based on how curry and uncurry work together (a -> b -> c to (a, b) -> c), it causes the state portion to fall on the right of the Pair. It also requires the state s to be fixed to a type for the "maths" to work out. Only the resultant can vary in its type. Because of that, it is common in crocks and many other languages for the Functor portion (the part that can change its type) of an ADT to be on the far right, so for the type siggy, that pushes that to the far right State s a. s MUST be fixed and a can vary its type.

The long, mathy version:

One way State can be explained is with an Adjunction between Set and Set (or in our case, JS and JS), with the Left being a Functor from what I call the resultant (a) to Pair a s (with s being the state type). s is chosen by the Functor and is fixed to its type, but as it is a Functor the type a can vary. On Diagrams you may see this functor labeled as (- x S, with the - as the varying resultant type)

The reason it is on the right side of the Pair is because to the Right portion of the Adjunction, back into Set (or JS again in our case). This Functor maps type a to function s -> a. So this will map ANY type a (including a the Pair from above. You will see Diagrams represent this as -^S, again with the part that varies called - here.

So if we follow the adjunction by composing the two Functors L(R a) and pick Number for our s that will gives us a structure (A x Number)^Number or a function Number -> Pair A Number.

Briisk Sp. z o.o.
Briisk Sp. z o.o.
~ 6 years ago

@Briisk Very keen observation. The answer is hard to explain as I do not know your understanding of Category Theory.

Thanks a lot for the detailed reply (-: After I posted the question I figured out that it might have something to do with currying but I wanted to be sure about it. This helped to understand it.

Dean
Dean
~ 5 years ago

Hey, I am curious about your "startsWith" method. I am curious how the seed value is getting into the state prior to you running your compose method. Does crocks flow from "right" to "left". The way it is reading is that I'd expect "startsWith" to be first to plant the "seed", THEN we act on the states value. Since it is last in the chain, I just want to verify it's reading right to left or is their some inner magic going on with crocks?

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

Hey, I am curious about your "startsWith" method. I am curious how the seed value is getting into the state prior to you running your compose method. Does crocks flow from "right" to "left". The way it is reading is that I'd expect "startsWith" to be first to plant the "seed", THEN we act on the states value. Since it is last in the chain, I just want to verify it's reading right to left or is their some inner magic going on with crocks?

Howdy Dean, thanks for the question! Was this for another lesson? I am having a hard time lining this up with the code presented.

EDIT: Also I do not see startsWith in this course, do you mean runWith?

Markdown supported.
Become a member to join the discussionEnroll Today