SF Scala Meetup - July 30, 2020
John A. De Goes — @jdegoes
Adam Fraser — @adamfraser
Refactoring Functional
Type Classes
WHY YOU’RE
HERE
“Let me tell you why you’re here. You’re here
because you know something. What you know
you can’t explain, but you feel it. You’ve felt it
your entire life, that there’s something wrong
with the world. You don’t know what it is, but
it’s there, like a splinter in your mind, driving
you mad. It is this feeling that has brought you
to me. Do you know what I’m talking about?”
— Morpheus, The Matrix
TABLE OF
CONTENTS
01
THE LEGEND OF FUNCTOR
The supreme reign of the Haskell functor hierarchy
02
TROUBLE IN FUNCTOR TOWN
Drawbacks of the classic functor hierarchy
03
EASY ALGEBRA
Unlike category theory, you already know algebra
04
TOUR OF ZIO PRELUDE
An algebraic, modular, & Scala-first basis for type classes
THE LEGEND
OF FUNCTOR
Functor
THE LEGEND
OF FUNCTOR
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
Functor -> Apply
THE LEGEND
OF FUNCTOR
trait Apply[F[_]] extends Functor[F] {
def map[A, B](fa: F[A])(f: A => B): F[B]
def ap[A, B](ff: F[A => B])(f: F[A]): F[B]
}
Functor -> Apply -> Applicative
THE LEGEND
OF FUNCTOR
trait Applicative[F[_]] extends Apply[F] {
def map[A, B](fa: F[A])(f: A => B): F[B]
def ap[A, B](ff: F[A => B])(f: F[A]): F[B]
def pure[A](a: A): F[A]
}
Functor -> Apply -> Applicative -> Monad
THE LEGEND
OF FUNCTOR
trait Monad[F[_]] extends Applicative[F] {
def map[A, B](fa: F[A])(f: A => B): F[B]
def ap[A, B](ff: F[A => B])(f: F[A]): F[B]
def pure[A](a: A): F[A]
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
}
*Bind is skipped.
THE LEGEND OF
FUNCTOR
The Ubiquity of Functor in
Functional Programming
● Haskell
● Scala
○ Scalaz
○ Cats
● Kotlin
● Java
● F#
● Idris
● And many others!
9
Books Have Been Written About It
THE LEGEND
OF FUNCTOR
TROUBLE IN
FUNCTOR TOWN
The Curse of Haskellisms
TROUBLE IN
FUNCTOR TOWN
def ap[A, B](
ff: F[A => B], fa: F[A]): F[B]
f :: Int -> Int -> Int -> Int
f <$> arg1 <*> arg2 <*> arg3
Set Is Not A “Functor”?
TROUBLE IN
FUNCTOR TOWN
set.map(f).map(g)
set.map(f andThen g)
f g f.andThen(g)
A B C A C
The Functor Hierarchy Is A Lie!
TROUBLE IN
FUNCTOR TOWN
Functor in FP
Functor in Math
ZIO Config
TROUBLE IN
FUNCTOR TOWN
(string(“server”) |@|
int(“port”))(
Config.apply(_),
Config.unapply(_))
ZIO Codec
TROUBLE IN
FUNCTOR TOWN
lazy val js: Codec[Val] =
((spacing, ()) ~>
(obj | arr | str | `true` |
`false` | `null` | num)
<~ ((), spacing))
Introspectable Monads
TROUBLE IN
FUNCTOR TOWN for {
bool <- boolParser
value <- if (bool) parserA
else parserB
} yield value
Constrained DSLs
TROUBLE IN
FUNCTOR TOWN
def map[A, B](fa: F[A])(f: A => B): F[B]
val fa: F[A] = …
val f : A => B = …
fa.map(a => f(a))
Type B is unconstrainable!
Invariance Pain
TROUBLE IN
FUNCTOR TOWN
val functorDog: F[Dog] = …
val functorAnimal: F[Animal] =
functorDog
ERROR!!!
Invariance Pain
TROUBLE IN
FUNCTOR TOWN
val functorDog: F[Dog] = …
val functorAnimal: F[Animal] =
functorDog.widen[Animal]
Stack-Exploding Strictness
TROUBLE IN
FUNCTOR TOWN
def forever[A](fa: F[A]): F[A] =
fa *> forever(fa)
Lawless Type Classes & Operations
TROUBLE IN
FUNCTOR TOWN
Defer
Foldable
Monad#tailRecM
Parallel
NonEmptyParallel
UnorderedFoldable
Type Class Proliferation
TROUBLE IN
FUNCTOR TOWN
Align
Alternative
Always
Applicative
ApplicativeError
Apply
Bifoldable
Bifunctor
Bimonad
Bitraverse
CoflatMap
CommutativeApplicative
CommutativeApply
CommutativeFlatMap
CommutativeMonad
Comonad
Contravariant
ContravariantMonoidal
ContravariantSemigroupal
Defer
Distributive
Eval
EvalGroup
EvalMonoid
EvalSemigroup
FlatMap
Foldable
Functor
FunctorFilter
Inject
InjectK
Invariant
InvariantMonoidal
InvariantSemigroupal
Later
Monad
MonadError
MonoidK
NonEmptyParallelNonEmptyR
educible
NonEmptyTraverse
NotNull
Now
Parallel
Reducible
Representable
SemigroupK
Semigroupal
Show
StackSafeMonad
Traverse
TraverseFilter
UnorderedFoldable
UnorderedTraverse
Type Class Proliferation
TROUBLE IN
FUNCTOR TOWN
Align
Alternative
Always
Applicative
ApplicativeError
Apply
Bifoldable
Bifunctor
Bimonad
Bitraverse
CoflatMap
CommutativeApplicative
CommutativeApply
CommutativeFlatMap
CommutativeMonad
Comonad
Contravariant
ContravariantMonoidal
ContravariantSemigroupal
Defer
Distributive
Eval
EvalGroup
EvalMonoid
EvalSemigroup
FlatMap
Foldable
Functor
FunctorFilter
Inject
InjectK
Invariant
InvariantMonoidal
InvariantSemigroupal
Later
Monad
MonadError
MonoidK
NonEmptyParallelNonEmptyR
educible
NonEmptyTraverse
NotNull
Now
Parallel
Reducible
Representable
SemigroupK
Semigroupal
Show
StackSafeMonad
Traverse
TraverseFilter
UnorderedFoldable
UnorderedTraverse
*meme from dev.to
Maddening Monad Transformers
TROUBLE IN
FUNCTOR TOWN
type MyApp[E, W, S, R, A] =
OptionT[
EitherT[
WriterT[
StateT[
Kleisli[Task, R, *],
S, *],
W, *],
E, *],
*]
TROUBLE IN
FUNCTOR TOWN
Is There Another Way?
EASY ALGEBRA
EASY ALGEBRA
// Set of elements
type Int
// Operations on those elements
def plus(x: Int, y: Int): Int
// Laws about those operations
x + (y + z) == (x + y) + z
x + y == y + x
You Already Know This
EASY ALGEBRA
Associativity
// Set of elements
type A
// Operations on those elements
def combine(l: A, r: A): A
// Laws about those operations
a1 <> (a2 <> a3) == (a1 <> a2) <> a3
EASY ALGEBRA
Identity
// Set of elements
type A
// Operations on those elements
def combine(l: A, r: A): A
val identity: A
// Laws about those operations
a <> identity == a
identity <> a == a
EASY ALGEBRA
Commutativity
// Set of elements
type A
// Operations on those elements
def combine(l: A, r: A): A
// Laws about those operations
a1 <> a2 == a2 <> a1
EASY ALGEBRA
EASY ALGEBRA
Standard Types
// Associative, commutative, identity
def combine(l: Int, r: Int): Int =
l + r
val identity: Int = 0
// Associative, identity
def combine(l: String, r: String): String =
l + r
val identity: String = “”
EASY ALGEBRA
Business Domain Specific Types
final case class Csv(
rows: Vector[Vector[String]],
headers: Map[String, Int]
)
// Associative, identity
def combine(l: Csv, r: Csv): Csv =
???
TOUR OF ZIO
PRELUDE
> PRELUDE
ZIO Prelude is a small library that brings a
common, useful algebraic abstractions & data
types to Scala developers.
ZIO Prelude is an alternative to libraries like
Scalaz and Cats based on radical ideas that
embrace modularity & subtyping in Scala and
offer new levels of power and ergonomics.
TOUR OF ZIO
PRELUDE
TOUR OF ZIO
PRELUDE
Radical Orthogonal Principled
Scala-First Minimal
Pragmatic
Accessible Opinionated
Guiding Design Principles
TOUR OF ZIO
PRELUDE Data Structures &
Patterns for
Traversing
List[A], Option[A], ...
Patterns of
Composition for
Types
(A, A) => A
Patterns of
Composition for
Type Constructors
(F[A], F[A]) => F[A]
Three Areas of Focus
TOUR OF ZIO
PRELUDE
The Anti-Modularity of the Functor Hierarchy
Functor
Applicative, Monad, etc.
Trouble starts here!
TOUR OF ZIO
PRELUDE
The Anti-Modularity of the Functor Hierarchy
Functions Composition
The Classic Functor
Hierarchy
TOUR OF ZIO
PRELUDE
The Anti-Modularity of the Functor Hierarchy
trait Monad[F[_]] extends Applicative[F] {
def map[A, B](fa: F[A])(f: A => B): F[B]
def ap[A, B](ff: F[A => B])(f: F[A]): F[B]
def pure[A](a: A): F[A]
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
}
TOUR OF ZIO
PRELUDE
Detangling Functions from Composition
trait Monad[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
def zip[A, B](fa: F[A], fb: F[B]): F[(A, B)]
def any: F[Any]
def flatten[A](fa: F[F[A]]): F[A]
}
TOUR OF ZIO
PRELUDE
The Modularity of the ZIO Prelude Hierarchy
Functions Composition
ZIO Prelude
Hierarchy
TOUR OF ZIO
PRELUDE
The Modularity of the ZIO Prelude Hierarchy
type Semigroup[A] = Associative[A]
type CommutativeSemigroup[A] = Associative[A] with Commutative[A]
type Monoid[A] = Identity[A]
type CommutativeMonoid[A] = Commutative[A] with Identity[A]
type Group[A] = Identity[A] with Inverse[A]
type AbelianGroup[A] = Commutative[A] with Identity[A] with Inverse[A]
TOUR OF ZIO
PRELUDE
The Modularity of the ZIO Prelude Hierarchy
type Functor[F[+_]] = Covariant[F]
type Contravariant[F[-_]] = zio.prelude.Contravariant[F]
type Invariant[F[_]] = zio.prelude.Invariant[F]
type Alternative[F[+_]] = Covariant[F] with IdentityBoth[F] with IdentityEither[F]
type InvariantAlt[F[_]] = Invariant[F] with IdentityBoth[F] with IdentityEither[F]
TOUR OF ZIO
PRELUDE
The Modularity of the ZIO Prelude Hierarchy
type InvariantSemigroupal[F[_]] = Invariant[F] with AssociativeBoth[F]
type Semigroupal[F[+_]] = Covariant[F] with AssociativeBoth[F]
type ContravariantSemigroupal[F[-_]] = Contravariant[F] with AssociativeBoth[F]
type SemigroupK[F[_]] = AssociativeEither[F]
type MonoidK[F[_]] = IdentityEither[F]
type ContravariantMonoidal[F[-_]] = Contravariant[F] with IdentityBoth[F]
type InvariantMonoidal[F[_]] = Invariant[F] with IdentityBoth[F]
TOUR OF ZIO
PRELUDE
The Modularity of the ZIO Prelude Hierarchy
type FlatMap[F[+_]] = Covariant[F] with AssociativeFlatten[F]
type Monad[F[+_]] = Covariant[F] with IdentityFlatten[F]
type Divide[F[-_]] = Contravariant[F] with AssociativeBoth[F]
type Divisible[F[-_]] = Contravariant[F] with IdentityBoth[F]
type Decidable[F[-_]] = Contravariant[F] with IdentityBoth[F] with IdentityEither[F]
type Apply[F[+_]] = Covariant[F] with AssociativeBoth[F]
type Applicative[F[+_]] = Covariant[F] with IdentityBoth[F]
type InvariantApplicative[F[_]] = Invariant[F] with IdentityBoth[F]
TOUR OF ZIO
PRELUDE
trait Associative[A] {
def combine(l: A, r: A): A
}
// a1 <> (a2 <> a3) == (a1 <> a2) <> a3
TOUR OF ZIO
PRELUDE
trait Identity[A] extends Associative[A] {
def combine(l: A, r: A): A
def identity: A
}
// a <> identity == a
// identity <> a == a
TOUR OF ZIO
PRELUDE
trait Commutative[A] extends Associative[A] {
def combine(l: A, r: A): A
}
// a1 <> a2 == a2 <> a1
TOUR OF ZIO
PRELUDE
trait Covariant[F[+_]] {
def map[A, B](f: A => B):
F[A] => F[B]
}
Variance Guarantees Automatic Widening
TOUR OF ZIO
PRELUDE
trait Contravariant[F[-_]] {
def contramap[A, B](f: B => A):
F[A] => F[B]
}
Variance Guarantees Automatic Narrowing
TOUR OF ZIO
PRELUDE
case class <=>[A, B](
to: A => B, from: B => A)
trait Invariant[F[_]] {
def invmap[A, B](f: A <=> B):
F[A] <=> F[B]
}
TOUR OF ZIO
PRELUDE
trait AssociativeBoth[F[_]] {
def combine[A, B](
left : F[A],
right: F[B]): F[(A, B)]
}
// zio1 zip zio2
TOUR OF ZIO
PRELUDE
trait AssociativeEither[F[_]] {
def combine[A, B](
left : F[A],
right: F[B]): F[Either[A, B]]
}
// zio1 orElseEither zio2
TOUR OF ZIO
PRELUDE
trait AssociativeFlatten[F[+_]] {
def flatten[A](nested: F[F[A]]): F[A]
}
// zio.flatten
TOUR OF ZIO
PRELUDE
trait IdentityBoth[F[_]] extends
AssociativeBoth[F] {
def combine[A, B](
left : F[A],
right: F[B]): F[(A, B)]
def any: F[Any]
}
// zio1 zip zio2
// ZIO.unit
TOUR OF ZIO
PRELUDE
trait IdentityEither[F[_]] extends
AssociativeBoth[F] {
def combine[A, B](
left : F[A],
right: F[B]): F[Either[A, B]]
def none: F[Nothing]
}
// zio1 orElseEither zio2
// ZIO.halt(Cause.empty)
TOUR OF ZIO
PRELUDE
trait IdentityFlatten[F[+_]] extends
AssociativeFlatten[F] {
def flatten[A](nested: F[F[A]]): F[A]
def any: F[Any]
}
// zio.flatten
// ZIO.unit
TOUR OF ZIO
PRELUDE
trait CommutativeBoth[F[_]] extends
AssociativeBoth[F] {
def combine[A, B](
left : F[A],
right: F[B]): F[(A, B)]
}
// zio1 zipPar zio2
TOUR OF ZIO
PRELUDE
trait CommutativeEither[F[_]] extends
AssociativeEither[F] {
def combine[A, B](
left : F[A],
right: F[B]): F[Either[A, B]]
}
// zio1 raceEither zio2
TOUR OF ZIO
PRELUDE
trait Traversable[Data[+_]] {
def foreach[Effect[_]: …, A, B](as: Data[A])(
f: A => Effect[B]): Effect[Data[B]]
}
// requests.foreach { request =>
// handleRequest(request)
// }
TOUR OF ZIO
PRELUDE
trait NonEmptyTraversable[Data[+_]] extends
Traversable[Data] {
def foreach1[Effect[_]: …, A, B](as: Data[A])(
f: A => Effect[B]): Effect[Data[B]]
}
TOUR OF ZIO
PRELUDE
Debug[-A]
Equal[-A]
Hash[-A]
Ord[-A]
Embracing Declaration-Site Variance
implicit val ordAnimal:
Ord[Animal] = …
if (dog1 <= dog2) {
// WORKS!!!
}
TOUR OF ZIO
PRELUDE
NonEmptyList[A]
Validation[E, A]
ZSet[M, A]
Embrace & Extend Scala Collections
TOUR OF ZIO
PRELUDE
trait ZPure[-StateIn, +StateOut, -Env, +Err, +Success]
type State[S, +A] = ZPure[S, S, Any, Nothing, A]
type EState[S, +E, +A] = ZPure[S, S, Any, E , A]
No More Monad Transformers
For when you think ZIO is great but just doesn’t have enough type parameters
TOUR OF ZIO
PRELUDE
type MyStack[S1, S2, R, E, A] =
Kleisli[
({ type lambda[a] =
EitherT[
({ type lambda[a] =
IndexedStateT[Eval, S1, S2, a]
})#lambda,
E,
a]
})#lambda,
R,
A]
The Alternative
TOUR OF ZIO
PRELUDE
// Monad Transformers
def get[S, R, E]: MyStack[S, S, R, E, S] = {
type SState[A] = State[S, A]
type EitherESState[A] = EitherT[SState, E, A]
val eitherT = EitherT.liftF[SState, E, S](State.get)
Kleisli.liftF[EitherESState, R, S](eitherT)
}
// ZPure
def get[S]: State[S, S] =
State.get[S]
Ergonomics
TOUR OF ZIO
PRELUDE
Performance
TOUR OF ZIO
PRELUDE
Newtypes
object Meter extends Subtype[Int]
type Meter = Meter.Type
object Sum extends SubtypeF
type Sum[A] = Sum.Type[A]
object Natural extends
NewtypeSmart[Int](isGreaterThanEqualTo(0))
type Natural = Natural.Type
TOUR OF ZIO
PRELUDE
Ergonomics
List(1, 2, 3, 4, 5).foldMap(Sum(_))
// 15
List(1, 2, 3, 4, 5).foldMap(Prod(_))
// 120
● Documentation
● More Instances
● More Polishing
● Effect Type Classes
● Performance Optimization
● Automatic Derivation for ADTs
● Get Feedback from Real Users
NEXT STEPS
SPECIAL THANKS
● Dejan Mijic
● Sken
● Manfred Weber
● Jorge Aliss
● Phil Derome
● Kamal King
● Maxim Schuwalaw
And Salar Rahmanian!
THANK YOU!
Does anyone have any questions?
github.com/zio/zio-prelude
Get mentored: patreon.com/jdegoes
Follow us: @jdegoes, @adamfraser

Refactoring Functional Type Classes

  • 1.
    SF Scala Meetup- July 30, 2020 John A. De Goes — @jdegoes Adam Fraser — @adamfraser Refactoring Functional Type Classes
  • 2.
    WHY YOU’RE HERE “Let metell you why you’re here. You’re here because you know something. What you know you can’t explain, but you feel it. You’ve felt it your entire life, that there’s something wrong with the world. You don’t know what it is, but it’s there, like a splinter in your mind, driving you mad. It is this feeling that has brought you to me. Do you know what I’m talking about?” — Morpheus, The Matrix
  • 3.
    TABLE OF CONTENTS 01 THE LEGENDOF FUNCTOR The supreme reign of the Haskell functor hierarchy 02 TROUBLE IN FUNCTOR TOWN Drawbacks of the classic functor hierarchy 03 EASY ALGEBRA Unlike category theory, you already know algebra 04 TOUR OF ZIO PRELUDE An algebraic, modular, & Scala-first basis for type classes
  • 4.
  • 5.
    Functor THE LEGEND OF FUNCTOR traitFunctor[F[_]] { def map[A, B](fa: F[A])(f: A => B): F[B] }
  • 6.
    Functor -> Apply THELEGEND OF FUNCTOR trait Apply[F[_]] extends Functor[F] { def map[A, B](fa: F[A])(f: A => B): F[B] def ap[A, B](ff: F[A => B])(f: F[A]): F[B] }
  • 7.
    Functor -> Apply-> Applicative THE LEGEND OF FUNCTOR trait Applicative[F[_]] extends Apply[F] { def map[A, B](fa: F[A])(f: A => B): F[B] def ap[A, B](ff: F[A => B])(f: F[A]): F[B] def pure[A](a: A): F[A] }
  • 8.
    Functor -> Apply-> Applicative -> Monad THE LEGEND OF FUNCTOR trait Monad[F[_]] extends Applicative[F] { def map[A, B](fa: F[A])(f: A => B): F[B] def ap[A, B](ff: F[A => B])(f: F[A]): F[B] def pure[A](a: A): F[A] def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] } *Bind is skipped.
  • 9.
    THE LEGEND OF FUNCTOR TheUbiquity of Functor in Functional Programming ● Haskell ● Scala ○ Scalaz ○ Cats ● Kotlin ● Java ● F# ● Idris ● And many others! 9
  • 10.
    Books Have BeenWritten About It THE LEGEND OF FUNCTOR
  • 11.
  • 12.
    The Curse ofHaskellisms TROUBLE IN FUNCTOR TOWN def ap[A, B]( ff: F[A => B], fa: F[A]): F[B] f :: Int -> Int -> Int -> Int f <$> arg1 <*> arg2 <*> arg3
  • 13.
    Set Is NotA “Functor”? TROUBLE IN FUNCTOR TOWN set.map(f).map(g) set.map(f andThen g) f g f.andThen(g) A B C A C
  • 14.
    The Functor HierarchyIs A Lie! TROUBLE IN FUNCTOR TOWN Functor in FP Functor in Math
  • 15.
    ZIO Config TROUBLE IN FUNCTORTOWN (string(“server”) |@| int(“port”))( Config.apply(_), Config.unapply(_))
  • 16.
    ZIO Codec TROUBLE IN FUNCTORTOWN lazy val js: Codec[Val] = ((spacing, ()) ~> (obj | arr | str | `true` | `false` | `null` | num) <~ ((), spacing))
  • 17.
    Introspectable Monads TROUBLE IN FUNCTORTOWN for { bool <- boolParser value <- if (bool) parserA else parserB } yield value
  • 18.
    Constrained DSLs TROUBLE IN FUNCTORTOWN def map[A, B](fa: F[A])(f: A => B): F[B] val fa: F[A] = … val f : A => B = … fa.map(a => f(a)) Type B is unconstrainable!
  • 19.
    Invariance Pain TROUBLE IN FUNCTORTOWN val functorDog: F[Dog] = … val functorAnimal: F[Animal] = functorDog ERROR!!!
  • 20.
    Invariance Pain TROUBLE IN FUNCTORTOWN val functorDog: F[Dog] = … val functorAnimal: F[Animal] = functorDog.widen[Animal]
  • 21.
    Stack-Exploding Strictness TROUBLE IN FUNCTORTOWN def forever[A](fa: F[A]): F[A] = fa *> forever(fa)
  • 22.
    Lawless Type Classes& Operations TROUBLE IN FUNCTOR TOWN Defer Foldable Monad#tailRecM Parallel NonEmptyParallel UnorderedFoldable
  • 23.
    Type Class Proliferation TROUBLEIN FUNCTOR TOWN Align Alternative Always Applicative ApplicativeError Apply Bifoldable Bifunctor Bimonad Bitraverse CoflatMap CommutativeApplicative CommutativeApply CommutativeFlatMap CommutativeMonad Comonad Contravariant ContravariantMonoidal ContravariantSemigroupal Defer Distributive Eval EvalGroup EvalMonoid EvalSemigroup FlatMap Foldable Functor FunctorFilter Inject InjectK Invariant InvariantMonoidal InvariantSemigroupal Later Monad MonadError MonoidK NonEmptyParallelNonEmptyR educible NonEmptyTraverse NotNull Now Parallel Reducible Representable SemigroupK Semigroupal Show StackSafeMonad Traverse TraverseFilter UnorderedFoldable UnorderedTraverse
  • 24.
    Type Class Proliferation TROUBLEIN FUNCTOR TOWN Align Alternative Always Applicative ApplicativeError Apply Bifoldable Bifunctor Bimonad Bitraverse CoflatMap CommutativeApplicative CommutativeApply CommutativeFlatMap CommutativeMonad Comonad Contravariant ContravariantMonoidal ContravariantSemigroupal Defer Distributive Eval EvalGroup EvalMonoid EvalSemigroup FlatMap Foldable Functor FunctorFilter Inject InjectK Invariant InvariantMonoidal InvariantSemigroupal Later Monad MonadError MonoidK NonEmptyParallelNonEmptyR educible NonEmptyTraverse NotNull Now Parallel Reducible Representable SemigroupK Semigroupal Show StackSafeMonad Traverse TraverseFilter UnorderedFoldable UnorderedTraverse *meme from dev.to
  • 25.
    Maddening Monad Transformers TROUBLEIN FUNCTOR TOWN type MyApp[E, W, S, R, A] = OptionT[ EitherT[ WriterT[ StateT[ Kleisli[Task, R, *], S, *], W, *], E, *], *]
  • 26.
    TROUBLE IN FUNCTOR TOWN IsThere Another Way?
  • 27.
  • 28.
    EASY ALGEBRA // Setof elements type Int // Operations on those elements def plus(x: Int, y: Int): Int // Laws about those operations x + (y + z) == (x + y) + z x + y == y + x You Already Know This
  • 29.
    EASY ALGEBRA Associativity // Setof elements type A // Operations on those elements def combine(l: A, r: A): A // Laws about those operations a1 <> (a2 <> a3) == (a1 <> a2) <> a3
  • 30.
    EASY ALGEBRA Identity // Setof elements type A // Operations on those elements def combine(l: A, r: A): A val identity: A // Laws about those operations a <> identity == a identity <> a == a
  • 31.
    EASY ALGEBRA Commutativity // Setof elements type A // Operations on those elements def combine(l: A, r: A): A // Laws about those operations a1 <> a2 == a2 <> a1
  • 32.
  • 33.
    EASY ALGEBRA Standard Types //Associative, commutative, identity def combine(l: Int, r: Int): Int = l + r val identity: Int = 0 // Associative, identity def combine(l: String, r: String): String = l + r val identity: String = “”
  • 34.
    EASY ALGEBRA Business DomainSpecific Types final case class Csv( rows: Vector[Vector[String]], headers: Map[String, Int] ) // Associative, identity def combine(l: Csv, r: Csv): Csv = ???
  • 35.
  • 36.
    ZIO Prelude isa small library that brings a common, useful algebraic abstractions & data types to Scala developers. ZIO Prelude is an alternative to libraries like Scalaz and Cats based on radical ideas that embrace modularity & subtyping in Scala and offer new levels of power and ergonomics. TOUR OF ZIO PRELUDE
  • 37.
    TOUR OF ZIO PRELUDE RadicalOrthogonal Principled Scala-First Minimal Pragmatic Accessible Opinionated Guiding Design Principles
  • 38.
    TOUR OF ZIO PRELUDEData Structures & Patterns for Traversing List[A], Option[A], ... Patterns of Composition for Types (A, A) => A Patterns of Composition for Type Constructors (F[A], F[A]) => F[A] Three Areas of Focus
  • 39.
    TOUR OF ZIO PRELUDE TheAnti-Modularity of the Functor Hierarchy Functor Applicative, Monad, etc. Trouble starts here!
  • 40.
    TOUR OF ZIO PRELUDE TheAnti-Modularity of the Functor Hierarchy Functions Composition The Classic Functor Hierarchy
  • 41.
    TOUR OF ZIO PRELUDE TheAnti-Modularity of the Functor Hierarchy trait Monad[F[_]] extends Applicative[F] { def map[A, B](fa: F[A])(f: A => B): F[B] def ap[A, B](ff: F[A => B])(f: F[A]): F[B] def pure[A](a: A): F[A] def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] }
  • 42.
    TOUR OF ZIO PRELUDE DetanglingFunctions from Composition trait Monad[F[_]] { def map[A, B](fa: F[A])(f: A => B): F[B] def zip[A, B](fa: F[A], fb: F[B]): F[(A, B)] def any: F[Any] def flatten[A](fa: F[F[A]]): F[A] }
  • 43.
    TOUR OF ZIO PRELUDE TheModularity of the ZIO Prelude Hierarchy Functions Composition ZIO Prelude Hierarchy
  • 44.
    TOUR OF ZIO PRELUDE TheModularity of the ZIO Prelude Hierarchy type Semigroup[A] = Associative[A] type CommutativeSemigroup[A] = Associative[A] with Commutative[A] type Monoid[A] = Identity[A] type CommutativeMonoid[A] = Commutative[A] with Identity[A] type Group[A] = Identity[A] with Inverse[A] type AbelianGroup[A] = Commutative[A] with Identity[A] with Inverse[A]
  • 45.
    TOUR OF ZIO PRELUDE TheModularity of the ZIO Prelude Hierarchy type Functor[F[+_]] = Covariant[F] type Contravariant[F[-_]] = zio.prelude.Contravariant[F] type Invariant[F[_]] = zio.prelude.Invariant[F] type Alternative[F[+_]] = Covariant[F] with IdentityBoth[F] with IdentityEither[F] type InvariantAlt[F[_]] = Invariant[F] with IdentityBoth[F] with IdentityEither[F]
  • 46.
    TOUR OF ZIO PRELUDE TheModularity of the ZIO Prelude Hierarchy type InvariantSemigroupal[F[_]] = Invariant[F] with AssociativeBoth[F] type Semigroupal[F[+_]] = Covariant[F] with AssociativeBoth[F] type ContravariantSemigroupal[F[-_]] = Contravariant[F] with AssociativeBoth[F] type SemigroupK[F[_]] = AssociativeEither[F] type MonoidK[F[_]] = IdentityEither[F] type ContravariantMonoidal[F[-_]] = Contravariant[F] with IdentityBoth[F] type InvariantMonoidal[F[_]] = Invariant[F] with IdentityBoth[F]
  • 47.
    TOUR OF ZIO PRELUDE TheModularity of the ZIO Prelude Hierarchy type FlatMap[F[+_]] = Covariant[F] with AssociativeFlatten[F] type Monad[F[+_]] = Covariant[F] with IdentityFlatten[F] type Divide[F[-_]] = Contravariant[F] with AssociativeBoth[F] type Divisible[F[-_]] = Contravariant[F] with IdentityBoth[F] type Decidable[F[-_]] = Contravariant[F] with IdentityBoth[F] with IdentityEither[F] type Apply[F[+_]] = Covariant[F] with AssociativeBoth[F] type Applicative[F[+_]] = Covariant[F] with IdentityBoth[F] type InvariantApplicative[F[_]] = Invariant[F] with IdentityBoth[F]
  • 48.
    TOUR OF ZIO PRELUDE traitAssociative[A] { def combine(l: A, r: A): A } // a1 <> (a2 <> a3) == (a1 <> a2) <> a3
  • 49.
    TOUR OF ZIO PRELUDE traitIdentity[A] extends Associative[A] { def combine(l: A, r: A): A def identity: A } // a <> identity == a // identity <> a == a
  • 50.
    TOUR OF ZIO PRELUDE traitCommutative[A] extends Associative[A] { def combine(l: A, r: A): A } // a1 <> a2 == a2 <> a1
  • 51.
    TOUR OF ZIO PRELUDE traitCovariant[F[+_]] { def map[A, B](f: A => B): F[A] => F[B] } Variance Guarantees Automatic Widening
  • 52.
    TOUR OF ZIO PRELUDE traitContravariant[F[-_]] { def contramap[A, B](f: B => A): F[A] => F[B] } Variance Guarantees Automatic Narrowing
  • 53.
    TOUR OF ZIO PRELUDE caseclass <=>[A, B]( to: A => B, from: B => A) trait Invariant[F[_]] { def invmap[A, B](f: A <=> B): F[A] <=> F[B] }
  • 54.
    TOUR OF ZIO PRELUDE traitAssociativeBoth[F[_]] { def combine[A, B]( left : F[A], right: F[B]): F[(A, B)] } // zio1 zip zio2
  • 55.
    TOUR OF ZIO PRELUDE traitAssociativeEither[F[_]] { def combine[A, B]( left : F[A], right: F[B]): F[Either[A, B]] } // zio1 orElseEither zio2
  • 56.
    TOUR OF ZIO PRELUDE traitAssociativeFlatten[F[+_]] { def flatten[A](nested: F[F[A]]): F[A] } // zio.flatten
  • 57.
    TOUR OF ZIO PRELUDE traitIdentityBoth[F[_]] extends AssociativeBoth[F] { def combine[A, B]( left : F[A], right: F[B]): F[(A, B)] def any: F[Any] } // zio1 zip zio2 // ZIO.unit
  • 58.
    TOUR OF ZIO PRELUDE traitIdentityEither[F[_]] extends AssociativeBoth[F] { def combine[A, B]( left : F[A], right: F[B]): F[Either[A, B]] def none: F[Nothing] } // zio1 orElseEither zio2 // ZIO.halt(Cause.empty)
  • 59.
    TOUR OF ZIO PRELUDE traitIdentityFlatten[F[+_]] extends AssociativeFlatten[F] { def flatten[A](nested: F[F[A]]): F[A] def any: F[Any] } // zio.flatten // ZIO.unit
  • 60.
    TOUR OF ZIO PRELUDE traitCommutativeBoth[F[_]] extends AssociativeBoth[F] { def combine[A, B]( left : F[A], right: F[B]): F[(A, B)] } // zio1 zipPar zio2
  • 61.
    TOUR OF ZIO PRELUDE traitCommutativeEither[F[_]] extends AssociativeEither[F] { def combine[A, B]( left : F[A], right: F[B]): F[Either[A, B]] } // zio1 raceEither zio2
  • 62.
    TOUR OF ZIO PRELUDE traitTraversable[Data[+_]] { def foreach[Effect[_]: …, A, B](as: Data[A])( f: A => Effect[B]): Effect[Data[B]] } // requests.foreach { request => // handleRequest(request) // }
  • 63.
    TOUR OF ZIO PRELUDE traitNonEmptyTraversable[Data[+_]] extends Traversable[Data] { def foreach1[Effect[_]: …, A, B](as: Data[A])( f: A => Effect[B]): Effect[Data[B]] }
  • 64.
    TOUR OF ZIO PRELUDE Debug[-A] Equal[-A] Hash[-A] Ord[-A] EmbracingDeclaration-Site Variance implicit val ordAnimal: Ord[Animal] = … if (dog1 <= dog2) { // WORKS!!! }
  • 65.
    TOUR OF ZIO PRELUDE NonEmptyList[A] Validation[E,A] ZSet[M, A] Embrace & Extend Scala Collections
  • 66.
    TOUR OF ZIO PRELUDE traitZPure[-StateIn, +StateOut, -Env, +Err, +Success] type State[S, +A] = ZPure[S, S, Any, Nothing, A] type EState[S, +E, +A] = ZPure[S, S, Any, E , A] No More Monad Transformers For when you think ZIO is great but just doesn’t have enough type parameters
  • 67.
    TOUR OF ZIO PRELUDE typeMyStack[S1, S2, R, E, A] = Kleisli[ ({ type lambda[a] = EitherT[ ({ type lambda[a] = IndexedStateT[Eval, S1, S2, a] })#lambda, E, a] })#lambda, R, A] The Alternative
  • 68.
    TOUR OF ZIO PRELUDE //Monad Transformers def get[S, R, E]: MyStack[S, S, R, E, S] = { type SState[A] = State[S, A] type EitherESState[A] = EitherT[SState, E, A] val eitherT = EitherT.liftF[SState, E, S](State.get) Kleisli.liftF[EitherESState, R, S](eitherT) } // ZPure def get[S]: State[S, S] = State.get[S] Ergonomics
  • 69.
  • 70.
    TOUR OF ZIO PRELUDE Newtypes objectMeter extends Subtype[Int] type Meter = Meter.Type object Sum extends SubtypeF type Sum[A] = Sum.Type[A] object Natural extends NewtypeSmart[Int](isGreaterThanEqualTo(0)) type Natural = Natural.Type
  • 71.
    TOUR OF ZIO PRELUDE Ergonomics List(1,2, 3, 4, 5).foldMap(Sum(_)) // 15 List(1, 2, 3, 4, 5).foldMap(Prod(_)) // 120
  • 72.
    ● Documentation ● MoreInstances ● More Polishing ● Effect Type Classes ● Performance Optimization ● Automatic Derivation for ADTs ● Get Feedback from Real Users NEXT STEPS
  • 73.
    SPECIAL THANKS ● DejanMijic ● Sken ● Manfred Weber ● Jorge Aliss ● Phil Derome ● Kamal King ● Maxim Schuwalaw And Salar Rahmanian!
  • 74.
    THANK YOU! Does anyonehave any questions? github.com/zio/zio-prelude Get mentored: patreon.com/jdegoes Follow us: @jdegoes, @adamfraser