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.

Checkpoint 1: changing the value inside a Kind

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

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.

Checkpoint 2: a step that can fail, on a Path

On an EitherPath, which method runs a step that can itself fail? Does that method go through Either's Monad instance?

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.

Checkpoint 3: nothing to give back

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

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".

Checkpoint 4: what a future's handler receives

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

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.

Checkpoint 5: three lookups on Try

How many different objects do Instances.monad(try_()), Instances.applicative(try_()) and Instances.functor(try_()) give back?

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.

Checkpoint 6: a lookup inside map

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

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.

Checkpoint 7: every bad line, or the first

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

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.

Checkpoint 8: widening twice

You widen the same Maybe twice, and the same Optional twice. Is each pair the same object?

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.

Checkpoint 9: par or from, on a list

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

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.

Checkpoint 10: write countLines

Write countLines, which gives the number of lines of the order in any container. Ask for the least capability it needs.

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.

Checkpoint 11: write receiptLine

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

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.

Checkpoint 12: write fromCacheOr

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

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?

See Example Code

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 scoreNext step
10 to 12, with Checkpoints 10 to 12 among themYou 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 11Reread the sections you missed, try those questions again, then carry on as for 10 to 12
5 or fewerGo 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