Using put
to update our state for a given state transaction can make it difficult to modify a given state based on its previous value. In this lesson we will explore a means to lift specialized functions that can be used modify our state’s value. These specialized functions must have the same type in their input and output. We take a look at a construction helper named modify
that is used to remove the boilerplate of this type of construction. We also start to see how we can extend simple discrete stateful transactions to create other stateful transactions.
Instructor: [00:00] A common construction when working with stateful computations allows us to update our state based upon its previous value. We bring in both a State and Pair constructors from crocks to see how this could be accomplished. We'll also define this initial state, which is an object that has a key of bubbles with the number of 42 as its value.
[00:19] We'll start off by making a construction helper that takes a function defining how our state should be modified, which we'll call modifyState. ModifyState is defined as a function that takes a special function that accepts and returns the same type of s and gives us back an instant State s of unit.
[00:37] Before we start implementing our helper, we first need to bring the unit constructor into scope for our usage.
[00:44] With the usual suspects all rounded up, let's implement modifyState to accept a function fn and then promptly return a state instance that will take in the current State s and return a pair with unit in the resultant and the result of calling our function fn with the current state as our new state. Before we can call modifyState, we need an Endo function to define our modification.
[01:09] What we're looking to do is increase our bubbles value on our state by 1. We need a function that takes and returns an object, as our state is fixed to the type of object. Crocks provides a binary function named mapProps that not only provides a means for our modification but matches the Object -> Object signature to boot.
[01:29] To use mapProps, we need to partially apply an object of functions that will replace a given key's value with the result of applying the original value to the provided function. Here we'll target the key of bubbles but now need a function that will map its value by adding 1 to it.
[01:45] It just so happens we have this curried add helper which returns the result of two numbers under addition. We can now plug it in partially applying the number 1 to it, but before we can run this, we've got to bring it into scope. To do this, we'll destructure it off of our nifty helpers object that we require in. We're good to go.
[02:06] Now we give it a save and see we get back our instance just itching for some state to run. Let's oblige by calling evalWith with our initial state, save it down again. We see we get back our boring unit in the resultant. But if we call execWith with the same state, we see our state has been modified to now be an object with our bubbles incremented to 43.
[02:31] Let's capture this state modification by creating a function that we'll call blowBubble. We'll define blowBubble as a function that goes from unit to a state with object in the state portion and unit in the resultant.
[02:45] To implement this, it's as easy as ripping out our transaction from below and dropping it in as the body of blowBubble. Now, with our function implemented, we give it a call passing in Nothing and save down our changes to see that we still have our result but with a much better name.
[03:03] This type of interaction with a state is so common when working with stateful computations that the State constructor provides a construction helper named modify to do the heavy lifting for us. We gain access to this helper by destructuring modify off of the State constructor and send these folks packing, as they're no longer needed.
[03:22] We also get rid of our homegrown helper and update blowBubble to use modify instead. After saving this down, we see we still get back our bubbles 43, although one thing that is bumming me out right now is that blowBubbles seems like a one-off function. There's not much we can do with it.
[03:41] Let's see if we can remedy this morose state of being and find some contentment in this new blowBubbles function. We'll define blowBubbles as a function that instead of taking a unit takes a number, returning a state with an object as the state and unit as the resultant. Now we take a number n as input and move our transaction into the body, replacing the partially applied 1 with our n.
[04:07] Using our new function, blowBubble got a whole lot simpler -- as it is now, just a call to blowBubbles passing 1. Now we save it down and see that everything is still working as expected on the state side. A quick peek into our resultant shows our unit.
[04:22] Why stop here? Now that we've parameterized our helper, we can reuse it in so many ways. Just by copying it down and making some slight modifications, we can create a whole mess of functionality that does the opposite.
[04:35] To implement, we'll make a new function named burstBubbles that still takes a number n for the input, but in our body we now call blowBubbles, passing the additive inverse of our n. Let's give this function a play by jumping downstairs and replacing blowBubble with a call to burstBubbles, providing 10 as our input. After a quick save, we now have 32 bubbles.
[04:59] We can now reuse this function to create a little buddy for blowBubble, getting even more usage out of our single-state transaction and thus demonstrating the power of parameterization. We'll name this new function burstBubble and replace the body to call burstBubbles instead, keeping 1 as the input.
[05:17] After jumping downstairs and calling burstBubble with Nothing, we get our expected 41 bubbles.
There is a really good course on egghead that lets you build an intuition on these datatypes.
https://egghead.io/courses/professor-frisby-introduces-composable-functional-javascript
State is just one of many datatypes, and the "labels" like: Functor, Applicative and Monad are ways to describe properties about these datatypes.
Most of the datatypes used out there (Maybe
, Either
, Async
(Future), State
) abstract away or encapsulate some sort of effect, behavior or structure.
Maybe
, for instance, is a way to represent disjunction (logical or) with the false side of the disjunction all mapping to a singleton type (like undefined or null).
The effect that State provides is the ability to either read from or modify a shared state.
For us programmers/developers/etc, The fact that something is a Monad means that we have a way to combine the effects of (2) instances of that datatype. In JS (using the fantasy-land spec), this means that a datatype will have a chain
method and an of
static function on the constructor. So when we chain
a function, it will combine the effects.
For something like Maybe, that may be thought of as nested if
s without else cases. For State, chaining allows use to combine multiple reads/writes to the provided state.
While that may make no sense now, it is something to keep in mind as you go through Brian's course.
is there a lesson series that explains what Monads are? I feel so lost by all of these terms and the current docs of crocks is not suited for a novice.