Traversals

Traversals represent a way to filter a collection of objects that we encounter along our path and continue extracting values from the individual objects in the collection, glomming the results together into a collection in the output. For example, in our intro, matching was a traversal that filtered the friends list down to just the users who had gold member status, and then we were able to extract all of their names into a list. This sort of behavior is some of the most compelling in shades, so we definitely want to see how to type it.

What’s a Traversal? Well it’s just a Traversal

Traversal describes the shape of a Traversal object. The odd thing is that we don’t actually need it to contain anything. It’s just going to act as a marker, a signal to get that this position in the path is going to be a collection of Item . Because of this, we’ll be able to use the same object to handle any collection type (such as an Array, an Object, ES2015 Maps and Sets, and even Immutable.js collections). For our example, we’ll just use arrays to keep it simple (for now).

V3: A traversal and a string walk into a bar

Let’s start with a motivating example. We’ll filter down our user’s friends to those who have more than five friends, then extract all their names into a list. We’ll do the filtering with a function matching that takes a filter function from A to boolean and produces a Traversal<A> :

Let’s think about this before we dive in. We are first going to perform a traversal over our collection, which means we are filtering down the array in some way. But filtering doesn’t change the output type at all, so so far, smooth sailing. But next we are going to extract out the names, so we’ll end up with a list of strings. A naïve attempt at this will run into problems:

We’re getting a never type back from our KeyAt in the return type. This is because S doesn’t represent a User object. It’s a User[] . The only keys we could extract from it are things like length , map , etc. We can fix this with a little acrobatics:

Where’s Waldo but for type signatures

Do you see the difference? We changed our type constraint S to be about the elements of the collection, and then just said that our input would be an array of S .

This works very well and is a handy trick for when we know about the structure a container is going to have (an Array in this case), and we want to constrain or refer to the element type. However, it has a big downside, and what’s more, I’ve already said it in that last sentence. It requires us to know the structure the container is going to have. We want to be able to write generic functions that can work over many different types of containers such as Maps, Sets, Arrays, and Objects simultaneously. And for this, we need to pull out the big guns.