1. 13
    Randomly Pull an Item from an Array with the State ADT
    4m 39s

Randomly Pull an Item from an Array with the State ADT

Share this video with your friends

Social Share Links

Send Tweet
Published 6 years ago
Updated 6 years ago

Functor composition is a powerful concept that arises when we have one Functor nested in another Functor. It becomes even more powerful when both of those are Chains, allowing us to apply each Functor’s special properties and effects for a given computation.

We witness this power first hand by combining our beloved State with a Pair type mixing the ability to randomly pick an index from an Array using State and use it to draw an element from the Array. We will build up to an easy to use interface that allows us to pull as many elements as we need, by chaining on single State transaction.

Instructor: [00:00] We've imported this state transition named generateCards, which uses these lists in the state to generate 12 unique cards. Using this seed, we would like to randomly pull 1 of those 12 cards. Calling evalWith against our state to take a peek in the resultant, we see our cards just sitting there, doing nothing. 12 lazy, old cards, to be exact.

[00:20] Let's give them something to do. We would like to place them in this deck type, defined as a pair of array of card, so we can draw from them. From our game model, we'll export a new state transition that we'll call getDeck, that takes nothing as its input. We define getDeck as a function that takes a unit to a state appState with our deck type in the resultant.

[00:43] To implement, we start with a call to generateCards to get our cards in the resultant. To track our progress, we import getDeck in our index, replacing our call down under, verifying we have our cards. With our cards in the resultant, we can map a lambda that takes in the cards and pairs them with an empty array on the left.

[01:03] With our deck sitting firmly in the resultant, we see our cards on the right, verifying their length of 12. On the left, we have an empty array to place the drawn card that will eventually pull from the right.

[01:15] With drawing on the mind, we'll pop back over to our game model and export a curried function named draw that takes an index first and then a deck as its second argument. We define draw as a function that takes an integer to an endomorphism, taking a deck to the same type deck.

[01:33] To implement, we take our given deck and chain on it using this handy helper function called drawCardAt that becomes a Kliesli for pair when we load it up for an integer as its first argument, which we do by giving it the index we were given.

[01:48] We can now give it a go with our getDeck function by bringing it into our index and using it to map our getDeck result, pulling the top card with an index of zero to see a nice orange square in our draw pile.

[02:01] A quick look-see at the right side of our deck shows our cards to be drawn, sans one card, for a length of 11. While not ideal, with this setup we can keep drawing cards from the top by mapping each time we want to pull, seeing our length climb on the left with each go.

[02:18] On the right, we see a length of nine, with our three cards deducted. Heck, drawing cards is so much fun, we draw another for giggles, then pop back to our game model to clean up our function a bit.

[02:29] We have what appears to be a composition with our index feeding drawCardAt, which in turn feeds chain that chains on our data. Using a point-free version of chain, we compose it after drawCardAt, which gets loaded with an index, resulting in a function that partially applies chain, returning a function ready for a deck, keeping our siggie the same.

[02:51] With our slick, new draw function, we now have everything we need to start leaning on our random number generator to pull a random card. We export a new drawRandom transition from our game model, which we define as a function that takes a deck to a state appState of deck, taking note of deck in both the input and in our state's resultant.

[03:13] Draw takes two arguments. We reach for the Crocks converge combinator and merge our paths with liftA2 partially applied with our draw function. To derive our random index, we pull from our random model this randomIndex function that uses the seed in our state to select a random index from a given array, placing it on the left portion of converge, leaving us with a type mismatch.

[03:36] RandomIndex expects an array for its argument, but we'll be passing in a deck. This is easily fixed with a composition that grabs from the right side of our deck with SND before calling randomIndex.

[03:49] The second argument can get the deck it needs with the identity combinator. Because of liftA2, we need to lift it into a state instance with our liftState function.

[03:59] We now have a much better interface for drawing cards that can be chained with our state instances, which we demonstrate over in our index file by chaining it with our getDeck result to see we get back a yellow square. Yet another chain yields us a fancy blue square. Yet another adds a handsome orange triangle.

[04:19] Thing is, no matter how many times we pull, we get back the same random order every time. If we change our seed of 23 to something a bit more dynamic -- like Date.now -- things feel a bit more random. Perfect. With five cards drawn, we can check the right side to see our expected length of seven.