Obtaining Instances: The Instances Facade
Ask one class for every type class instance, in one shape: Instances.monad(maybe()).
Each type class in Higher-Kinded-J has a standard instance for each type, and org.higherkindedj.hkt.instances.Instances is where you ask for it. Every lookup has the same shape, Instances.x(...), so typing Instances. in an IDE lists every capability on offer.
- Ask
Instancesfor aFunctor,Applicative,MonadorMonadErrorwith a token fromWitnesses - Pass the dependency an instance needs, such as a
Semigroup, aMonoidor an outerMonad - Tell the lookups the compiler checks from the three that can fail at run time
- Name the error type a
MonadErrorreally has
InstancesFacadeBookTest.java holds each claim this page makes, including that its tables match every token and every method.
Zero-argument lookups
For a type whose instance needs nothing, pass its token: a method in Witnesses, such as maybe(), that names the type's witness.
import static org.higherkindedj.hkt.instances.Witnesses.maybe;
import org.higherkindedj.hkt.Applicative;
import org.higherkindedj.hkt.Functor;
import org.higherkindedj.hkt.Monad;
import org.higherkindedj.hkt.instances.Instances;
import org.higherkindedj.hkt.maybe.MaybeKind;
Monad<MaybeKind.Witness> monad = Instances.monad(maybe());
Applicative<MaybeKind.Witness> applicative = Instances.applicative(maybe());
Functor<MaybeKind.Witness> functor = Instances.functor(maybe());
The instance implements the whole Functor, Applicative, Monad chain in one object, so all three calls return that same object.
Three more lookups give a richer capability, for the types whose instance has it. MonadError adds raising and recovering from an error; MonadZero adds an empty value and filter; Alternative adds an empty value and orElse. For Maybe and Optional, orElse falls back to a second value when the first is empty; for List, Stream and VStream, it joins the two.
MonadError<MaybeKind.Witness, Unit> me = Instances.monadError(maybe());
MonadZero<ListKind.Witness> mz = Instances.monadZero(list());
Alternative<OptionalKind.Witness> alt = Instances.alternative(optional());
| Lookup | Type | Error type |
|---|---|---|
monadError | Maybe, Optional | Unit |
monadError | Try, CompletableFuture, VTask | Throwable |
monadError | Either<L, _> | L |
monadZero | Maybe, Optional, List, Stream | |
alternative | Maybe, Optional, List, Stream, VStream |
The compiler cannot check that a type has the capability you ask for. A type without it, as in Instances.monadZero(either()), throws ClassCastException when the call runs. A wrong error type compiles too: MonadError<MaybeKind.Witness, String> throws ClassCastException once you raise a String. Take E from the table.
Type parameters come from your variable
Each token has a generic type, Of<F>, rather than a Class. Java has no generic class literals, so a Class would lose Either's left type or Reader's environment. The four tokens for types with such a parameter, either(), reader(), context() and state(), are generic methods, so the parameter comes from the variable you assign to:
// OrderError flows from the assignment target
Monad<EitherKind.Witness<OrderError>> m = Instances.monad(either());
Available tokens
| Family | Tokens |
|---|---|
| No type parameter | maybe(), io(), list(), nonEmptyList(), optional(), try_(), vtask(), vstream(), lazy(), stream(), completableFuture(), trampoline(), id() |
| A type parameter | either(), reader(), context(), state() |
try_() ends in an underscore because try is a reserved word in Java.
Argument-carrying instances
Validated, EitherOrBoth, Writer and the monad transformers each need a dependency: a Semigroup, which combines two values, a Monoid, which is a Semigroup with an empty value, or an outer Monad. None has a sensible default, so the facade asks for it in the method's parameters, where the compiler checks it and the IDE shows it:
MonadError<ValidatedKind.Witness<List<String>>, List<String>> v = Instances.validated(Semigroups.list());
Monad<EitherOrBothKind.Witness<List<String>>> eob = Instances.eitherOrBoth(Semigroups.list());
Monad<WriterKind.Witness<String>> w = Instances.writer(Monoids.string());
MonadError<EitherTKind.Witness<OptionalKind.Witness, String>, String> et =
Instances.eitherT(Instances.monad(optional()));
MonadError<MaybeTKind.Witness<OptionalKind.Witness>, Unit> mt =
Instances.maybeT(Instances.monad(optional()));
MonadError<OptionalTKind.Witness<MaybeKind.Witness>, Unit> ot =
Instances.optionalT(Instances.monad(maybe()));
Monad<ReaderTKind.Witness<OptionalKind.Witness, String>> rt =
Instances.readerT(Instances.monad(optional()));
Monad<StateTKind.Witness<Integer, OptionalKind.Witness>> st =
Instances.stateT(Instances.monad(optional()));
MonadWriter<WriterTKind.Witness<OptionalKind.Witness, String>, String> wt =
Instances.writerT(Instances.monad(optional()), Monoids.string());
A dependency in the signature cannot be missed: Instances.validated() with no Semigroup does not compile.
You can now get any built-in instance in one shape: a token for a type that needs nothing, an argument for one that needs a dependency. The rest of this page is for what the compiler checks, what the facade covers, and the accessors behind it.
Which of these throws, and when: Instances.monad(io()) or Instances.alternative(io())?
Answer and why
Answer and why
Instances.alternative(io()) throws ClassCastException when the call runs, and Instances.monad(io()) succeeds. The compiler cannot check that a type has the capability you ask for, and IO's instance has no Alternative.
Where this lives: Zero-argument lookups.
maybe() is a token for Instances.monad(maybe()). Is there a validated() token, for Instances.monad(validated())?
var monad = Instances.monad(validated());
Answer and why
Answer and why
No: javac reports "cannot find symbol". Validated needs a Semigroup to join its errors, so it has no token. The facade asks for the Semigroup in the method's parameters instead: Instances.validated(Semigroups.list()).
Where this lives: Argument-carrying instances.
The fine print
What the compiler checks
Instances is a class of static methods, with no registry, no ServiceLoader and nothing to configure. A token lookup returns the instance its token carries, and a lookup that takes an argument builds a new instance on each call.
monad,applicativeandfunctorare checked. Every token carries aMonad, so these three always succeed.monadError,monadZeroandalternativecast, and the compiler cannot check the cast. The warning in Zero-argument lookups shows what fails, and when.
The accessors behind the facade
Each class behind the facade has an accessor of its own, in one of four shapes. Instances is the one shape to use in new code:
Semigroup<String> semi = Semigroups.string();
Monad<OptionalKind.Witness> outer = OptionalMonad.INSTANCE;
Monad<MaybeKind.Witness> m1 = MaybeMonad.INSTANCE; // a static field
Monad<EitherKind.Witness<String>> m2 = EitherMonad.instance(); // a generic static method
MonadError<ValidatedKind.Witness<String>, String> v =
ValidatedMonad.instance(semi); // a factory with an argument
MonadError<EitherTKind.Witness<OptionalKind.Witness, String>, String> et =
new EitherTMonad<>(outer); // a constructor with an argument
Scope
| Type class | Through Instances |
|---|---|
Functor, Applicative, Monad | Yes, for every token |
MonadError, MonadZero, Alternative | Yes, for the types in the table in Zero-argument lookups |
The instances that need an argument: validated, eitherOrBoth, writer, eitherT, maybeT, optionalT, readerT, stateT, writerT | Yes |
MonadWriter | Only through writerT |
MonadReader, MonadState | Not offered: ReaderTMonadReader and StateTMonadState are separate classes |
Traverse, Selective, Foldable | Not offered: each is a separate instance class, such as MaybeTraverse.INSTANCE |
Free, FreeApplicative | Not offered: use new FreeMonad<>() or FreeApApplicative.instance() |
Const | Not offered: it has an Applicative and a Bifunctor, and a token carries a Monad |
Semigroup, Monoid | Not offered: they are keyed by a value type, not a witness; use Semigroups and Monoids |
Bifunctor, Profunctor | Not offered: they take a binary witness |
See Also
Previous: Working with Kind Next: Functor