The resultant in a State ADT instance can be used as a means of communication between different stateful transactions. It can be used to read and transform a portion of our state into a form that another transaction is dependent on. This allows us to only keep what is needed state, without filling it with calculations that are only needed for one or few transactions.
We take can take advantage of this by pulling not only a random number using the seed from our state, but also pulling a list of card and filtering them, to randomly select one from our calculated state. Then we use the resultant to store the calculation before we save the result to state.
Instructor: [00:00] To randomly select a hint from a collection of unselected cards, we'll need to do three things, first filter our cards, randomly select one, and finally set the hint. In our state, we have a cards collection that will contain cards with a selected attribute set to true to mark a card selected.
[00:17] We also have a seed attribute for seeding our random number generator and finally a hint attribute, which we'll set with our randomly selected hint. Let's go about implementing our first task of filtering our list of cards.
[00:30] Over in our turn model, we export a new function called getUnselectedCards, that takes a unit as its input. We'll define our new state transaction as a function that goes from unit to a state appState of array of card.
[00:47] We'll reach for the selectState helper function, pointing at our cards attribute, that we'll use to run the Crocks filter function over its array, filtering out selected cards by using this notSelected predicate, which we partially apply to filter, placing our filtered list in the resultant.
[01:05] That just about does it for our first task. We'll now end up with a list in our resultant that only contains cards that do not have selected set to true. We'll import this transaction into our index from data model turn and replace our state downstairs with a call to getUnselectedCards.
[01:22] With a quick save, we get our expected state instance, which we'll run with evalWith on our initial state to see we have a problem, Houston. We wanted to have an array of card, but we have a maybe of array of card due to the selectState function returning a maybe.
[01:37] No matter. We duck under our state with map and option out our maybe with an empty array. Much better. We find that the selected blue circle is not included in our filtered list. If we also select this green square, we find it missing as well.
[01:53] Anything selected will not show up in our filtered list, which is exactly what we want. With that in the bag, we now can move on to the task of using our random number generator to select a card from our filtered list.
[02:05] Over in our random model, we'll exploit this between function to select a number between two integers, basically the start and length of our array. As we're selecting an index, we export a function called randomIndex, which we'll define as a function that takes an array of any type A and gives us back a state appState instance of integer.
[02:26] RandomIndex will take in its array and then return the result of calling between with a starting index of zero and a length of the array for its length, giving us back a state instance with a random integer in the resultant, which we can use in our turn model to randomly pick an element from the filtered list we get back from getUnselectedCards.
[02:46] After a quick import to pull randomIndex in from our random model, we can now create a function that we'll call Captain Jean-Luc pickCard to make it so. We define this brand-new state transaction as a Kleisli arrow that will take our array of card from getUnselectedCards to a state appState of card, picking one from our filtered list.
[03:06] Taking full advantage of the applicative nature of state, we reach for converge and liftA2 to lift this binary helper function getAt, which pulls an element from an array at a specified index.
[03:18] Our index will come from randomIndex on the left portion and will lift the identity combinator to form a Kleisli on the right side to apply the array to the second argument for getAt. With our new pickCard transaction hungry for unselected cards to choose from, we'll export a transaction called nextHint to combine the first two steps of our tasks.
[03:39] We define it as a Kliesli that takes a unit to a state appState of card, placing the selected card in its resultant. We reach for the Crocks composeK helper to create a Kliesli composition with pickCard being fed by the result of getUnselectedCards, leaving us with a random card in the resultant of the resulting state instance.
[04:01] Back in our index file, we can give nextHint a go. Pulling it off of our turn module and replacing getUnselectedCards down below, we see that we are in fact getting what seems to be a random card in our resultant with each save, which now brings us to the final step of setting our hint with our selected card.
[04:20] Back in our turn model, we need yet another stateful transaction that takes a card as its input and will transform it into a hint before setting it in our state. We call it setHint and define it as a function that takes a card to an instance of state appState of unit.
[04:39] We implement with this over function pointed at hint and use the Crocks constant combinator to create a function that will always return the result of calling this toHint function, which will provide an object with only color and shape keys on it. All we need to do now is give toHint the card from our resultant to ensure only hints end up in our hint attribute.
[05:01] To round this off, we need to add setHint to our nextHint composition after pickCard, giving us the expected unit in our resultant. To peep the state back in index, we call execWith to pull the transition state and then focus in on hint to see that we now get random hints with each and every save we make.