Example: collapsing a list
Takeaway: Not all traits with implicit instances are typeclasses
Extensible | Generics | Runtime safe | |
Reflection | ✅ | ✅ | ❌ |
Pattern matching | ❌ | ❌ | ✅ |
Subtyping | ❌ | ❌ | ✅ |
Typeclasses | ✅ | ✅ | ✅ |
Standard library examples (pre-2.13)
*-kinded
String, Int, Boolean, List[Int]
higher-kinded (* -> *)
Option[_], List[_], Future[_]
higher-kinded types are "incomplete" - they have a hole
also called "type constructors"
Type-level functions
f = x => f(x) //function lambda
Option = [A] => Option[A] //type lambda
Higher-kinded types need to be applied with a type to become a *-kinded type
|
|
Unfortunately, IDEA can't (yet) :(
* - not a precise definition, but it'll suffice
t
can be substituted by its value a
, then it's
referentially transparent
//if a function f(x) is referentially transparent
//and x is referentially transparent
val a = f(x)
(a, a) <-> (f(x), f(x))
IO
Find the side effect
(line 13, limit)
def f[A](a: A): A
has one
def f9: IO[Unit]
def f9: IO[Unit]
Core typelevel project
Multiversal equality
Note: typeclass definitions in the slides are simplified
Tip: use consistent import style to avoid conflicts
Write a function that checks if all elements of a List[T]
are equal
Comparison, ordering
Write a function that finds the largest element of a List[T]
Text representation - type-safe toString
Write an instance of Show[List[T]]
binary operation + (with monoid) identity element
map
, abstracted
There are two rules of the functor club.
//identity law
fa.map(id) == fa
//composition law
fa.map(f).map(g) == fa.map(f.andThen(g))
Write a function that adds 42
to all elements in a functor
(potentially parallel) combination of independent effects, wrapping value in effect
Sequential composition of effects
//left identity
pure(a).flatMap(f) == f(a)
//right identity
fa.flatMap(pure) == fa
//associativity
fa.flatMap(f).flatMap(g) == fa.flatMap(a => f(a).flatMap(g))
raising errors, recovering from them
Running predicates, transforming errors, un-recovering
sequence
flips effects
fa.traverse(f) == fa.map(f).sequence
fa.sequence == fa.traverse(identity)
A list that's guaranteed not to be empty
With safe `head`, `tail` operations
like Either, but with error accumulation
type ValidatedNel[+E, +A] = Validated[NonEmptyList[E], A]
Useful for error accumulation, because NEL has a semigroup
type EitherNel[+E, +A] = Either[NonEmptyList[E], A]
IO
, or error accumulation
like Validated
Later - parallel IO
An IO monad for cats (and more)
An IO, when evaluated, will do one of these:
IO.never
)
IO without async capabilities
Shifts execution to a thread pool
Usually created with an EC (for IO)
Schedules delayed tasks (sleep), has a clock
Stream[F[_], A]
F[_]
producing 0..infinity
values of type A
Get in touch