Check Your Understanding
Twelve questions on the Ship pages, each answer proved by the build.
These questions cover the pages from One Line, Six Layers to For Comprehensions. They start with recall and end with writing code of your own, and they do not follow page order, on purpose. Answer each question before you open its answer, in your head or on paper. Each answer ends with a link to the section that teaches it, and Where to go next turns your score into a plan.
You hold a Kind<MaybeKind.Witness, Order>. What do you call map on to work out the order's total, and how do you get a plain Maybe back?
Answer and why
Answer and why
On Maybe's instance, such as Instances.functor(maybe()), and then MAYBE.narrow. Any of the functor, applicative or monad lookups gives that instance. A Kind has no methods of its own, so calling map on it does not compile: javac cannot find a "method map" on it.
Kind<MaybeKind.Witness, Order> found = MAYBE.widen(Maybe.just(order));
found.map(placed -> placed.customer());
Where this lives: The five steps.
On an EitherPath, which method runs a step that can itself fail? Does that method go through Either's Monad instance?
Answer and why
Answer and why
via, and no. An EitherPath holds an Either and calls it directly, with no Kind and no instance between. Monad's flatMap is the same operation over Kind, and the two give the same result.
Where this lives: Two encodings, one algebra.
A step runs only for its effect, and has no value to give back. What goes in the A of its Kind<F, A>, and why not Void?
Answer and why
Answer and why
Unit, whose one value is Unit.INSTANCE. Void's only value is null, and what of(null) does depends on the container: Either's throws, and Maybe's gives Nothing. Unit.INSTANCE is a value that every container holds.
Where this lives: Unit: a value for "nothing to return".
A CompletableFuture fails inside supplyAsync, and a later thenApply stage passes the failure on. What does handleErrorWith's handler receive: a CompletionException, or the exception the supplier threw?
Answer and why
Answer and why
The exception the supplier threw. CompletableFuture's instance unwraps the cause from any CompletionException before it calls the handler.
Where this lives: What each instance does.
How many different objects do Instances.monad(try_()), Instances.applicative(try_()) and Instances.functor(try_()) give back?
Answer and why
Answer and why
One. A token's instance implements the whole Functor, Applicative, Monad chain, so the three lookups return that same object, for Try as for Maybe.
Where this lives: Zero-argument lookups.
found is a Kind<EitherKind.Witness<String>, Order>, monad is Either's Monad, and findCustomer gives back a Kind<EitherKind.Witness<String>, Customer>. What does monad.map(findCustomer, found) give back, and which method gives a Kind<EitherKind.Witness<String>, Customer> instead?
Answer and why
Answer and why
A Kind inside a Kind, and flatMap. map puts whatever its function returns inside the container, a container included. flatMap runs a step that gives back a container, and flattens the result into one. The compiler agrees with both types:
Kind<EitherKind.Witness<String>, Kind<EitherKind.Witness<String>, Customer>> nested =
monad.map(findCustomer, found);
Kind<EitherKind.Witness<String>, Customer> flat = monad.flatMap(findCustomer, found);
Where this lives: What a Monad adds.
You traverse three line items with a check that fails on two of them, once through Instances.validated(Semigroups.list()) and once through Either's instance. How many errors come back from each?
Answer and why
Answer and why
Two from Validated, and one from Either. traverse combines the results with the instance it is given, so it keeps what that instance's map2 keeps. Validated's joins the errors with its semigroup, here one that keeps them all, and Either's keeps the first.
Where this lives: What each instance does, and What builds on Applicative.
You widen the same Maybe twice, and the same Optional twice. Is each pair the same object?
Answer and why
Answer and why
The Maybe pair is, and the Optional pair is not. A Maybe is its own Kind, so widening returns it. An Optional cannot implement Kind, so each widen wraps it in a new holder.
Where this lives: The pieces each type provides.
With List's instance, For.par starts a chain from the SKUs ["LAMP", "DESK"] and the quantities [1, 2]. How many values does yield give? Does a chain that binds the same two lists with from give a different count?
Answer and why
Answer and why
Four, and no. par combines with List's map2, which gives every combination. from uses List's flatMap, which runs the next step on every element, so it reaches the same four.
Where this lives: Independent steps with par, and What each instance does.
Write countLines, which gives the number of lines of the order in any container. Ask for the least capability it needs.
Answer and why
Answer and why
A Functor is enough, because counting needs only map:
// Only map is needed, so the method asks for a Functor
public static <F extends WitnessArity<TypeArity.Unary>> Kind<F, Integer> countLines(
Functor<F> functor, Kind<F, Order> orders) {
return functor.map(order -> order.lines().size(), orders);
}
Where this lives: One method for any container.
Write receiptLine, which builds "<order id> to <email>" from an order and an email address looked up independently, in any container. Ask for the least capability it needs, and say what it gives with Instances.validated(Semigroups.list()) when neither was found.
Answer and why
Answer and why
An Applicative, because the two values do not depend on each other, so map2 combines them:
// The order and the email are found apart, so the method asks for an Applicative
public static <F extends WitnessArity<TypeArity.Unary>> Kind<F, String> receiptLine(
Applicative<F> applicative, Kind<F, Order> order, Kind<F, EmailAddress> email) {
return applicative.map2(order, email, (o, e) -> o.id() + " to " + e.value());
}
With Validated's instance and a semigroup that joins lists, it gives both errors.
Where this lives: One method for any container.
Write fromCacheOr, which gives the order from a cache lookup, or calls the order service when the cache missed, in any container with an error type. The service must not be called when the cache has the order.
Answer and why
Answer and why
A MonadError, with the service call in a handler, so it runs only when the cache missed. recoverWith would take the service's result as a value, so Java would call the service first, whatever the cache gave:
// The service is called only when the cache missed, so the call sits in a handler
public static <F extends WitnessArity<TypeArity.Unary>, E> Kind<F, Order> fromCacheOr(
MonadError<F, E> monadError, Kind<F, Order> cached, Supplier<Kind<F, Order>> service) {
return monadError.handleErrorWith(cached, missed -> service.get());
}
Where this lives: Should you use recoverWith or handleErrorWith?
The answers' code is SelfCheckBook.java, and SelfCheckBookTest.java holds every result an answer predicts. The build also checks the refusal in Checkpoint 1 against javac's own words, and compiles the two types in Checkpoint 6. Open them after the twelve, since they hold the answers.
Where to go next
A question counts when every part of your answer was right before you opened it, and code you wrote counts when it asks for the same capability as the answer's and gives the same results. Whatever your score, open the Where this lives link of any answer you missed.
| Your score | Next step |
|---|---|
| 10 to 12, with Checkpoints 10 to 12 among them | You can ship code written once for any container. Read the rest of the chapter as a task calls for it: Higher-Kinded Types for the encoding in depth, Type Classes for Traverse, Selective and the rest, Core Types for each type, and the Quick Reference or FAQ when you hold a question |
| Any other score from 6 to 11 | Reread the sections you missed, try those questions again, then carry on as for 10 to 12 |
| 5 or fewer | Go back to One Line, Six Layers and read each Ship page through to For Comprehensions, working the Foundations journey alongside, then take the questions again |
Previous: For Comprehension Next: Higher-Kinded Types