Learning cats along with Scala with Cats
Type class is a design pattern that encapsulates the implementation to process a type T value. It roughly consists of three components: Type class, Type class instances and interface method. Interface method takes both type T value and an instance of the type class as its arguments and decouples concrete implementation to process type T value as a type class instance. So users can use the method to any type T by supplying type class instances to its implicit scope.
Type Class Variance | Invariant | Covariant | Contravariant |
---|---|---|---|
Supertype instance used? | No | No | Yes |
More specific type preferred? | No | Yes | No |
Monoid is a type class that has following properties.
- (A, A) => A
- associative
- has identity element
1.
means that Monoid is closed, that means it takes two arguments of the same type (A in above)
and produces a result that is also the same type of arguments.
Semigroup is a type class that has following properties
- (A, A) => A
- associative
Comparing with Monoid, Semigroup does not have any identity element. Conversely, Monoid is also Semigroup.
For instances, NonEmptyList
and PositiveNumber
does not have identity element so that it is Semigroup
but not Monoid
These properties are useful when combining values produced in parallel execution because their completion order is no matter.
A Functor is a type class taking type constructor as its type parameter that
transform an element of type A into type B within the context.
Functor F[A]
has the following property.
(A => B) => F[B]
Thus, functor chains sequential operations within the closed context.
A type constructor is a constructor that produce regular type by being filled a type placeholder.
List
is a type constructor that can produce regular type List[Int]
by filling a placeholder with Int
.
Contravariant Functor has the contramap that prepends an operation.
(B => A) => F[B]
When given the map function B => A, it can be prepended all methods in F[B]
thus F[B]
can be instantiated.
any methods m of F[B]
can be implemented using map function f: B => A as f andThen m
.
B => A means B is subtype of A.
It produces F[B]
from F[A]
i.e. F[A] => F[B]
, F[B]
is regarded as subtype of F[A]
.
Therefore it fills covariant's relationship.
Invariant Functor has the imap that transform A in bidirection.
(A <=> B) => F[B]
A Monad is a type class taking type constructor as its type parameter. Similar to Functor, it sequences computing but it can also begin new computing sequence on the middle of another sequence. Monad has following properties:
- pure:
A => F[A]
- flatMap:
(F[A])(A => F[B]) => F[B]
- MonadError
- handle errors in monadic way
- Eval
- eager/lazy evaluation and memorization
- Writer
- separate I/O and computation
- Reader
- computation with a placeholder to inject dependencies
- State
- a function that transforms an input state to an output state and then computes a result.
cats | Properties | |
---|---|---|
val | Now | eager,memorized |
def | Always | lazy, not memorized |
lazy val | Later | lazy, memorized |
A MonadTransformer wraps 2 nested Monads and provides reasonable flatMap definition according to inner Monad.
In cats, MonadTransformers are conventionally defined as {name of Monad}T
like OptionT.
Semigroupal is a type class that takes 2 parameters typed F[A]
, F[B]
and produce a tupled value within a context F[(A, B)]
.
It has following property:
- product:
(F[A], F[B]) => F[(A, B)]
- Validated
- accumulate left value by combining them using Semigroup
- ValidatedNel
- Validated with NonEmptyList as its left values.
Apply extends Semigroupal and Functor so it has following properties:
- product:
(F[A], F[B]) => F[(A, B)]
- ap:
(F[A => B])(F[A]) => F[B]
Applicative Functor extends Apply and has pure so it has following properties:
- product:
(F[A], F[B]) => F[(A, B)]
- ap:
(F[A => B])(F[A]) => F[B]
- pure:
A => F[A]
Refer to Cats infographic
Foldable is a type class that have foldLeft and foldRight that are flexible way to iterate elements in a context and returning a result.
- foldLeft:
(F[A], B)((B, A) => B) => B
- foldRight:
(F[A], B)((A, B) => B) => B
Traverse is more formulated than Foldable that it combines the result by Applicative#product.
- traverse:
(F[A])(A => G[B]): G[F[B]]
- sequence:
(F[G[B]]): G[F[B]]