class: center, middle # Things that are things, but not other things [Nicolas Rinaudo] • [@NicolasRinaudo@functional.cafe] ??? I want to talk about categorical abstractions - monads, and stuff. Not because this is a particularly original topic, but because, well. I've never been to Warsaw, I really wanted to, and submitting a talk on monads is usually a very good way of getting through a CFP, at least in the Scala community. So far, everything seems to be working according to plan. The problem, of course, is that I now have to find something interesting to say about monads. And one thing that I often found frustrating when learning about that family of abstractions is how just about everything is a monad - why even have all these intermediate abstractions if everything is all the things, all the time? This ties in to one of the ways I learn things: when given a definition of a thing, it helps me just as much to have examples of things that are *not* the thing as of things that are the thing. I really wished I'd had such examples of things that are things, but not other things, while learning abstractions. This talk is my attempt at providing such examples. --- ## Overview .center[] --- ## Overview .center[] --- ## Overview .center[] --- ## Overview .center[] --- ## Overview .center[] --- ## Overview .center[] --- class: center, middle # Warm up: not a Functor --- ## Functor .center[] --- ## Functor .center[] --- ## Functor .center[] ```scala def map[A, B](f: A => B)(`fa: F[A]`): F[B] ``` --- ## Functor .center[] ```scala def map[A, B](f: A => B)(fa: F[A]): `F[B]` ``` --- ## Functor .center[] ```scala def map[A, B](`f: A => B`)(fa: F[A]): F[B] ``` --- ## Functor .center[] ```scala def `map`[A, B](f: A => B)(fa: F[A]): F[B] ``` --- ## Functor .center[] ```scala def map[A, B](f: A => B)(fa: F[A]): F[B] ``` --- ## Function .center[] ```scala type F[A] = `X => A` def map[A, B](f: A => B)(fa: F[A]): F[B] = ??? ``` --- ## Function .center[] ```scala type F[A] = X => A def map[A, B](f: A => B)(`fa: F[A]`): F[B] = ??? ``` --- ## Function .center[] ```scala type F[A] = X => A def map[A, B](`f: A => B`)(fa: F[A]): F[B] = ??? ``` --- ## Function .center[] ```scala type F[A] = X => A def map[A, B](f: A => B)(fa: F[A]): `F[B]` = ??? ``` --- ## Function .center[] .diff-rm[ ```scala type F[A] = X => A def map[A, B](f: A => B)(fa: F[A]): F[B] = * `???` ``` ] --- ## Function .center[] .diff-add[ ```scala type F[A] = X => A def map[A, B](f: A => B)(fa: F[A]): F[B] = * `fa andThen f` ``` ] --- ## Breaking Function .center[] .diff-rm[ ```scala *type F[A] = `X => A` def map[A, B](f: A => B)(fa: F[A]): F[B] = fa andThen f ``` ] --- ## Breaking Function .center[] .diff-add[ ```scala *type F[A] = `A => X` def map[A, B](f: A => B)(fa: F[A]): F[B] = fa andThen f ``` ] --- ## Breaking Function .center[] .diff-rm[ ```scala type F[A] = A => X def map[A, B](f: A => B)(fa: F[A]): F[B] = * `fa andThen f` ``` ] --- ## Breaking Function .center[] .diff-rm[ ```scala type F[A] = A => X * *`def map[A, B](f: A => B)(fa: F[A]): F[B] =` * `fa andThen f` ``` ] --- ## Breaking Function .center[] ```scala type F[A] = A => X ``` --- ## Breaking Function .center[] .diff-rm[ ```scala *type F[A] = A => `X` ``` ] --- ## Breaking Function .center[] .diff-add[ ```scala *type F[A] = A => `Boolean` ``` ] --- ## Breaking Function .center[] .diff-rm[ ```scala *type `F[A]` = A => Boolean ``` ] --- ## Predicate .center[] .diff-add[ ```scala *type `Predicate[A]` = A => Boolean ``` ] --- ## Predicate .center[] ```scala type Predicate[A] = A => Boolean ``` --- ## Key takeaways A function parameterised on its: -- * output type is a `Functor`. -- * input type is *not* a `Functor`. --- class: center, middle # Interlude: product types --- ## Products .center[] ```scala case class `×`[A, B](first: A, second: B) ``` --- ## Products .center[] ```scala case class ×[A, B](`first: A`, second: B) ``` --- ## Products .center[] ```scala case class ×[A, B](first: A, `second: B`) ``` --- ## Product introduction .center[] ```scala def build[A, B](`a: A`, `b: B`): A×B = a×b ``` --- ## Product introduction .center[] ```scala def build[A, B](a: A, b: B): `A×B` = a×b ``` --- ## Product introduction .center[] ```scala def build[A, B](a: A, b: B): A×B = `a×b` ``` --- ## Product elimination .center[] ```scala def print[A, B](ab: `A×B`) = val a×b = ab println(s"$a and $b") ``` --- ## Product elimination .center[] ```scala def print[`A`, `B`](ab: A×B) = val a×b = ab println(s"$a and $b") ``` --- ## Product elimination .center[] ```scala def print[A, B](ab: A×B) = val `a×b` = ab println(s"$a and $b") ``` --- ## Products are functors .center[] ```scala type F[A] = `A×X` def map[A, B](f: A => B)(fa: F[A]): F[B] = ??? ``` --- ## Products are functors .center[] ```scala type F[A] = A×X def map[A, B](`f: A => B`)(fa: F[A]): F[B] = ??? ``` --- ## Products are functors .center[] ```scala type F[A] = A×X def map[A, B](f: A => B)(`fa: F[A]`): F[B] = ??? ``` --- ## Products are functors .center[] ```scala type F[A] = A×X def map[A, B](f: A => B)(fa: F[A]): `F[B]` = ??? ``` --- ## Products are functors .center[] .diff-rm[ ```scala type F[A] = A×X def map[A, B](f: A => B)(fa: F[A]): F[B] = * `???` ``` ] --- ## Products are functors .center[] .diff-add[ ```scala type F[A] = A×X def map[A, B](f: A => B)(fa: F[A]): F[B] = * `(??? : B)×(??? : X)` ``` ] --- ## Products are functors .center[] ```scala type F[A] = A×X def map[A, B](f: A => B)(`fa: F[A]`): F[B] = (??? : B)×(??? : X) ``` --- ## Products are functors .center[] .diff-add[ ```scala type F[A] = A×X def map[A, B](f: A => B)(fa: F[A]): F[B] = * `val (a×x) = fa` * (??? : B)×(??? : X) ``` ] --- ## Products are functors .center[] ```scala type F[A] = A×X def map[A, B](`f: A => B`)(fa: F[A]): F[B] = val (`a`×x) = fa (??? : B)×(??? : X) ``` --- ## Products are functors .center[] .diff-add[ ```scala type F[A] = A×X def map[A, B](f: A => B)(fa: F[A]): F[B] = val (a×x) = fa * `val b = f(a)` (??? : B)×(??? : X) ``` ] --- ## Products are functors .center[] ```scala type F[A] = A×X def map[A, B](f: A => B)(fa: F[A]): F[B] = val (a×`x`) = fa val `b` = f(a) (??? : B)×(??? : X) ``` --- ## Products are functors .center[] .diff-rm[ ```scala type F[A] = A×X def map[A, B](f: A => B)(fa: F[A]): F[B] = val (a×x) = fa val b = f(a) * `(??? : B)`×`(??? : X)` ``` ] --- ## Products are functors .center[] .diff-add[ ```scala type F[A] = A×X def map[A, B](f: A => B)(fa: F[A]): F[B] = val (a×x) = fa val b = f(a) * `b`×`x` ``` ] --- ## Products are functors .center[] ```scala type F[A] = A×X def map[A, B](f: A => B)(fa: F[A]): F[B] = val (a×x) = fa val b = f(a) b×x ``` --- ## Key takeaways A binary parametric product type is: -- * a `Functor`. --- class: center, middle # Functor but not Apply --- ## Apply .center[] --- ## Apply .center[] --- ## Apply .center[] ```scala def map2[A, B, C](f: (A, B) => C)(`fa: F[A], fb: F[B]`): F[C] ``` --- ## Apply .center[] ```scala def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): `F[C]` ``` --- ## Apply .center[] ```scala def map2[A, B, C](`f: (A, B) => C`)(fa: F[A], fb: F[B]): F[C] ``` --- ## Apply .center[] ```scala def `map2`[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] ``` --- ## Apply .center[] ```scala def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] ``` --- ## Product .center[] ```scala type F[A] = `A×X` def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] = ??? ``` --- ## Product .center[] ```scala type F[A] = A×X def map2[A, B, C](`f: (A, B) => C`)(fa: F[A], fb: F[B]): F[C] = ??? ``` --- ## Product .center[] ```scala type F[A] = A×X def map2[A, B, C](f: (A, B) => C)(`fa: F[A], fb: F[B]`): F[C] = ??? ``` --- ## Product .center[] ```scala type F[A] = A×X def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): `F[C]` = ??? ``` --- ## Product .center[] .diff-rm[ ```scala type F[A] = A×X def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] = * `???` ``` ] --- ## Product .center[] .diff-add[ ```scala type F[A] = A×X def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] = * `(??? : C)×(??? : X)` ``` ] --- ## Product .center[] ```scala type F[A] = A×X def map2[A, B, C](f: (A, B) => C)(`fa: F[A], fb: F[B]`): F[C] = (??? : C)×(??? : X) ``` --- ## Product .center[] ```scala type F[A] = A×X def map2[A, B, C](f: (A, B) => C)(`fa: F[A]`, `fb: F[B]`): F[C] = (??? : C)×(??? : X) ``` --- ## Product .center[] .diff-add[ ```scala type F[A] = A×X def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] = * `val (a×x1) = fa` * `val (b×x2) = fb` * (??? : C)×(??? : X) ``` ] --- ## Product .center[] ```scala type F[A] = A×X def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] = val (a×`x1`) = fa val (b×`x2`) = fb (??? : C)×(??? : X) ``` --- ## Product .center[] ```scala type F[A] = A×X def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] = val (`a`×x1) = fa val (`b`×x2) = fb (??? : C)×(??? : X) ``` --- ## Product .center[] ```scala type F[A] = A×X def map2[A, B, C](`f: (A, B) => C`)(fa: F[A], fb: F[B]): F[C] = val (`a`×x1) = fa val (`b`×x2) = fb (??? : C)×(??? : X) ``` --- ## Product .center[] .diff-add[ ```scala type F[A] = A×X def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] = val (a×x1) = fa val (b×x2) = fb * `val c = f(a, b)` (??? : C)×(??? : X) ``` ] --- ## Product .center[] ```scala type F[A] = A×X def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): `F[C]` = val (a×x1) = fa val (b×x2) = fb val c = f(a, b) (`??? : C`)×(`??? : X`) ``` --- ## Product .center[] ```scala type F[A] = A×X def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] = val (a×`x1`) = fa val (b×`x2`) = fb val c = f(a, b) (??? : C)×(`??? : X`) ``` --- ## Product .center[] .diff-add[ ```scala type F[A] = A×X def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] = val (a×x1) = fa val (b×x2) = fb val c = f(a, b) (??? : C)×(??? : X) * *`def combine(lhs: X, rhs: X): X =` * `???` ``` ] --- ## Product .center[] .diff-add[ ```scala type F[A] = A×X def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] = val (a×x1) = fa val (b×x2) = fb val c = f(a, b) * `val x3 = combine(x1, x2)` (??? : C)×(??? : X) def combine(lhs: X, rhs: X): X = ??? ``` ] --- ## Product .center[] ```scala type F[A] = A×X def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] = val (a×x1) = fa val (b×x2) = fb val `c` = f(a, b) val `x3` = combine(x1, x2) (??? : C)×(??? : X) def combine(lhs: X, rhs: X): X = ??? ``` --- ## Product .center[] .diff-rm[ ```scala type F[A] = A×X def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] = val (a×x1) = fa val (b×x2) = fb val c = f(a, b) val x3 = combine(x1, x2) * `(??? : C)`×`(??? : X)` def combine(lhs: X, rhs: X): X = ??? ``` ] --- ## Product .center[] .diff-add[ ```scala type F[A] = A×X def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] = val (a×x1) = fa val (b×x2) = fb val c = f(a, b) val x3 = combine(x1, x2) * `c`×`x3` def combine(lhs: X, rhs: X): X = ??? ``` ] --- ## Product .center[] ```scala type F[A] = A×X def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] = val (a×x1) = fa val (b×x2) = fb val c = f(a, b) val x3 = combine(x1, x2) c×x3 def combine(lhs: X, rhs: X): X = ??? ``` --- ## Breaking Product ```scala enum Label: case Front case Back case Bug case Feature ``` --- ## Breaking Product .center[] .diff-rm[ ```scala *type F[A] = A×`X` def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] = val (a×x1) = fa val (b×x2) = fb val c = f(a, b) val x3 = combine(x1, x2) c×x3 *def combine(lhs: `X`, rhs: `X`): `X` = ??? ``` ] --- ## Breaking Product .center[] .diff-add[ ```scala *type F[A] = A×`Label` def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] = val (a×x1) = fa val (b×x2) = fb val c = f(a, b) val x3 = combine(x1, x2) c×x3 *def combine(lhs: `Label`, rhs: `Label`): X = ??? ``` ] --- ## Breaking Product .center[] .diff-rm[ ```scala type F[A] = A×Label def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] = val (a×x1) = fa val (b×x2) = fb val c = f(a, b) val x3 = combine(x1, x2) c×x3 * *`def combine(lhs: Label, rhs: Label): Label =` * `???` ``` ] --- ## Breaking Product .center[] .diff-rm[ ```scala type F[A] = A×Label def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] = val (a×x1) = fa val (b×x2) = fb val c = f(a, b) * `val x3 = combine(x1, x2)` c×x3 * *`def combine(lhs: Label, rhs: Label): Label =` * `???` ``` ] --- ## Breaking Product .center[] .diff-rm[ ```scala type F[A] = A×Label def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] = val (a×x1) = fa val (b×x2) = fb val c = f(a, b) * `val x3 = combine(x1, x2)` * `c×x3` * *`def combine(lhs: Label, rhs: Label): Label =` * `???` ``` ] --- ## Breaking Product .center[] .diff-rm[ ```scala type F[A] = A×Label * *`def map2[A, B, C](f: (A, B) => C)(fa: F[A], fb: F[B]): F[C] =` * `val (a×x1) = fa` * `val (b×x2) = fb` * `val c = f(a, b)` * `val x3 = combine(x1, x2)` * * `c×x3` * *`def combine(lhs: Label, rhs: Label): Label =` * `???` ``` ] --- ## Breaking Product .center[] ```scala type F[A] = A×Label ``` --- ## Breaking Product .center[] .diff-rm[ ```scala type F[A] = A×Label ``` ] --- ## Breaking Product .center[] .diff-rm[ ```scala *type `F[A]` = A×Label ``` ] --- ## Labelled data .center[] .diff-add[ ```scala *type `Labelled[A]` = A×Label ``` ] --- ## Labelled data .center[] ```scala type Labelled[A] = A×Label ``` --- ## Key takeaways A binary parametric product type is: * a `Functor`. -- * not an `Apply` if the other member is not a `Semigroup`. --- class: center, middle # Apply but not Applicative --- ## Applicative .center[] --- ## Applicative .center[] --- ## Applicative .center[] ```scala def pure[A](`a: A`): F[A] ``` --- ## Applicative .center[] ```scala def pure[A](a: A): `F[A]` ``` --- ## Applicative .center[] ```scala def `pure`[A](a: A): F[A] ``` --- ## Product .center[] ```scala type F[A] = `A×X` def pure[A](a: A): F[A] = ??? ``` --- ## Product .center[] ```scala type F[A] = A×X def pure[A](`a: A`): F[A] = ??? ``` --- ## Product .center[] ```scala type F[A] = A×X def pure[A](a: A): `F[A]` = ??? ``` --- ## Product .center[] .diff-rm[ ```scala type F[A] = A×X def pure[A](a: A): F[A] = * `???` ``` ] --- ## Product .center[] .diff-add[ ```scala type F[A] = A×X def pure[A](a: A): F[A] = * `(??? : A)×(??? : X)` ``` ] --- ## Product .center[] ```scala type F[A] = A×X def pure[A](`a: A`): F[A] = (??? : A)×(??? : X) ``` --- ## Product .center[] ```scala type F[A] = A×X def pure[A](a: A): F[A] = (??? : A)×`(??? : X)` ``` --- ## Product .center[] .diff-add[ ```scala type F[A] = A×X def pure[A](a: A): F[A] = (??? : A)×(??? : X) * *`def empty: X =` * `???` ``` ] --- ## Product .center[] .diff-rm[ ```scala type F[A] = A×X def pure[A](a: A): F[A] = * `(??? : A)`×`(??? : X)` def empty: X = ??? ``` ] --- ## Product .center[] .diff-add[ ```scala type F[A] = A×X def pure[A](a: A): F[A] = * `a`×`empty` def empty: X = ??? ``` ] --- ## Product .center[] ```scala type F[A] = A×X def pure[A](a: A): F[A] = a×empty def empty: X = ??? ``` --- ## Breaking Product .center[] .diff-rm[ ```scala *type F[A] = A×`X` def pure[A](a: A): F[A] = a×empty *def empty: `X` = ??? ``` ] --- ## Breaking Product .center[] .diff-add[ ```scala *type F[A] = A×`PosInt` def pure[A](a: A): F[A] = a×empty *def empty: `PosInt` = ??? ``` ] --- ## Breaking Product .center[] .diff-rm[ ```scala type F[A] = A×PosInt def pure[A](a: A): F[A] = a×empty * *`def empty: PosInt =` * `???` ``` ] --- ## Breaking Product .center[] .diff-rm[ ```scala type F[A] = A×PosInt def pure[A](a: A): F[A] = * a×`empty` * *`def empty: PosInt =` * `???` ``` ] --- ## Breaking Product .center[] .diff-rm[ ```scala type F[A] = A×PosInt * *`def pure[A](a: A): F[A] =` * a×`empty` * *`def empty: PosInt =` * `???` ``` ] --- ## Breaking Product .center[] ```scala type F[A] = A×PosInt ``` --- ## Breaking Product .center[] .diff-rm[ ```scala *type `F[A]` = A×PosInt ``` ] --- ## Weighted data .center[] .diff-add[ ```scala *type `Weighted[A]` = A×PosInt ``` ] --- ## Weighted data .center[] ```scala type Weighted[A] = A×PosInt ``` --- ## Key takeaways A binary parametric product type is: * a `Functor`. * not an `Apply` if the other member is not a `Semigroup`. -- * not an `Applicative` if the other member is not a `Monoid`. --- class: center, middle # Apply but not FlatMap --- ## FlatMap .center[] --- ## FlatMap .center[] --- ## FlatMap .center[] ```scala def flatMap[B](f: A => F[B])(`fa: F[A]`): F[B] ``` --- ## FlatMap .center[] ```scala def flatMap[B](f: A => F[B])(fa: F[A]): `F[B]` ``` --- ## FlatMap .center[] ```scala def flatMap[B](`f: A => F[B]`)(fa: F[A]): F[B] ``` --- ## FlatMap .center[] ```scala def `flatMap`[B](f: A => F[B])(fa: F[A]): F[B] ``` --- ## FlatMap .center[] ```scala def flatMap[B](f: A => F[B])(fa: F[A]): F[B] ``` --- ## Product .center[] ```scala type F[A] = `A×X` def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = ??? ``` --- ## Product .center[] ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(`fa: F[A]`): F[B] = ??? ``` --- ## Product .center[] ```scala type F[A] = A×X def flatMap[A, B](`f: A => F[B]`)(fa: F[A]): F[B] = ??? ``` --- ## Product .center[] ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(fa: F[A]): `F[B]` = ??? ``` --- ## Product .center[] .diff-rm[ ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = * `???` ``` ] --- ## Product .center[] .diff-add[ ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = * `(??? : B)×(??? : X)` ``` ] --- ## Product .center[] ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(`fa: F[A]`): F[B] = (??? : B)×(??? : X) ``` --- ## Product .center[] .diff-add[ ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = * `val (a×x1) = fa` * (??? : B)×(??? : X) ``` ] --- ## Product .center[] ```scala type F[A] = A×X def flatMap[A, B](`f: A => F[B]`)(fa: F[A]): F[B] = val (`a`×x1) = fa (??? : B)×(??? : X) ``` --- ## Product .center[] .diff-add[ ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = val (a×x1) = fa * `val fb = f(a)` (??? : B)×(??? : X) ``` ] --- ## Product .center[] ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = val (a×`x1`) = fa val fb = f(a) (??? : B)×(??? : X) ``` --- ## Product .center[] ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = val (a×x1) = fa val `fb` = f(a) (??? : B)×(??? : X) ``` --- ## Product .center[] ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = val (a×x1) = fa val fb = f(a) (??? : B)×(??? : X) ``` --- ## Product .center[] .diff-rm[ ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = val (a×x1) = fa * val `fb` = f(a) (??? : B)×(??? : X) ``` ] --- ## Product .center[] .diff-add[ ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = val (a×x1) = fa * val `(b×x2)` = f(a) (??? : B)×(??? : X) ``` ] --- ## Product .center[] .diff-add[ ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = val (a×x1) = fa val (b×x2) = f(a) (??? : B)×(??? : X) * *`def combine(lhs: X, rhs: X): X =` * `???` ``` ] --- ## Product .center[] .diff-add[ ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = val (a×x1) = fa val (b×x2) = f(a) * `val x3 = combine(x1, x2)` (??? : B)×(??? : X) def combine(lhs: X, rhs: X): X = ??? ``` ] --- ## Product .center[] ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = val (a×x1) = fa val (`b`×x2) = f(a) val `x3` = combine(x1, x2) (??? : B)×(??? : X) def combine(lhs: X, rhs: X): X = ??? ``` --- ## Product .center[] .diff-rm[ ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = val (a×x1) = fa val (b×x2) = f(a) val x3 = combine(x1, x2) * `(??? : B)`×`(??? : X)` def combine(lhs: X, rhs: X): X = ??? ``` ] --- ## Product .center[] .diff-add[ ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = val (a×x1) = fa val (b×x2) = f(a) val x3 = combine(x1, x2) * `b`×`x3` def combine(lhs: X, rhs: X): X = ??? ``` ] --- ## Product .center[] ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = val (a×x1) = fa val (b×x2) = f(a) val x3 = combine(x1, x2) b×x3 def combine(lhs: X, rhs: X): X = ??? ``` --- ## Breaking Product .center[] ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = val (a×x1) = fa val (b×x2) = f(a) val x3 = combine(x1, x2) `b`×x3 def combine(lhs: X, rhs: X): X = ??? ``` --- ## Breaking Product .center[] ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = val (a×x1) = fa val `(b×x2)` = f(a) val x3 = combine(x1, x2) b×x3 def combine(lhs: X, rhs: X): X = ??? ``` --- ## Breaking Product .center[] ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = val (a×x1) = fa val (b×x2) = `f(a)` val x3 = combine(x1, x2) b×x3 def combine(lhs: X, rhs: X): X = ??? ``` --- ## Breaking Product .center[] ```scala type F[A] = A×X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = val `(a×x1)` = fa val (b×x2) = f(a) val x3 = combine(x1, x2) b×x3 def combine(lhs: X, rhs: X): X = ??? ``` --- ## Breaking Product .center[] ```scala type F[A] = `A`×X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = val (a×x1) = fa val (b×x2) = f(a) val x3 = combine(x1, x2) b×x3 def combine(lhs: X, rhs: X): X = ??? ``` --- ## Breaking Product .center[] .diff-rm[ ```scala *type F[A] = `A×`X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = val (a×x1) = fa val (b×x2) = f(a) val x3 = combine(x1, x2) b×x3 def combine(lhs: X, rhs: X): X = ??? ``` ] --- ## Breaking Product .center[] .diff-rm[ ```scala *type F[A] = `A×`X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = * val (`a`×x1) = fa val (b×x2) = f(a) val x3 = combine(x1, x2) b×x3 def combine(lhs: X, rhs: X): X = ??? ``` ] --- ## Breaking Product .center[] .diff-rm[ ```scala *type F[A] = `A×`X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = * val (`a`×x1) = fa * val (b×x2) = `f(a)` val x3 = combine(x1, x2) b×x3 def combine(lhs: X, rhs: X): X = ??? ``` ] --- ## Breaking Product .center[] .diff-rm[ ```scala *type F[A] = `A×`X def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] = * val (`a`×x1) = fa * `val (b×x2) = f(a)` val x3 = combine(x1, x2) b×x3 def combine(lhs: X, rhs: X): X = ??? ``` ] --- ## Breaking Product .center[] .diff-rm[ ```scala *type F[A] = `A×`X * *`def flatMap[A, B](f: A => F[B])(fa: F[A]): F[B] =` * `val (a×x1) = fa` * `val (b×x2) = f(a)` * `val x3 = combine(x1, x2)` * * `b×x3` def combine(lhs: X, rhs: X): X = ??? ``` ] --- ## Breaking Product .center[] ```scala type F[A] = X def combine(lhs: X, rhs: X): X = ??? ``` --- ## Breaking Product .center[] .diff-rm[ ```scala *type F[A] = `X` *def combine(lhs: `X`, rhs: `X`): `X` = ??? ``` ] --- ## Breaking Product .center[] .diff-add[ ```scala *type F[A] = `PosInt` *def combine(lhs: `PosInt`, rhs: `PosInt`): `PosInt` = ??? ``` ] --- ## Breaking Product .center[] ```scala type F[A] = PosInt def combine(lhs: PosInt, rhs: PosInt): PosInt = ??? ``` --- ## Breaking Product .center[] .diff-rm[ ```scala *type `F[A]` = PosInt def combine(lhs: PosInt, rhs: PosInt): PosInt = ??? ``` ] --- ## Weight .center[] .diff-add[ ```scala *type `Weight[A]` = PosInt def combine(lhs: PosInt, rhs: PosInt): PosInt = ??? ``` ] --- ## Weight .center[] ```scala type Weight[A] = PosInt def combine(lhs: PosInt, rhs: PosInt): PosInt = ??? ``` --- ## Key takeaways A binary parametric product type is: * a `Functor`. * not an `Apply` if the other member is not a `Semigroup`. * not an `Applicative` if the other member is not a `Monoid`. -- * not a `FlatMap` if an `Apply` with a phantom type. --- class: center, middle # Applicative but not Monad --- ## Monad .center[] --- ## Monad .center[] --- ## Flagged data .center[] ```scala type `Flagged[A]` = A×Boolean ``` --- ## Flagged data .center[] ```scala type Flagged[A] = `A`×Boolean ``` --- ## Flagged data .center[] ```scala type Flagged[A] = A×`Boolean` ``` --- ## Flagged data .center[] .diff-add[ ```scala type Flagged[A] = A×Boolean *`def pure[A](a: A): Flagged[A] =` * `a×false` ``` ] --- ## Flagged data .center[] ```scala type Flagged[A] = A×Boolean def pure[A](a: A): Flagged[A] = a×`false` ``` --- ## Flagged data .center[] .diff-add[ ```scala type Flagged[A] = A×Boolean def pure[A](a: A): Flagged[A] = a×false * *`def flatMap[A, B](f: A => Flagged[B])(fa: Flagged[A]): Flagged[B] =` * `val (a×x1) = fa` * `val (b×x2) = f(a)` * `val x3 = x1 || x2` * * `b×x3` ``` ] --- ## Flagged data .center[] ```scala type Flagged[A] = A×Boolean def pure[A](a: A): Flagged[A] = a×false def flatMap[A, B](f: A => Flagged[B])(fa: Flagged[A]): Flagged[B] = val (a×x1) = fa val (b×x2) = f(a) val x3 = x1 `||` x2 b×x3 ``` --- ## Flagged data .center[] ```scala type Flagged[A] = A×Boolean def pure[A](a: A): Flagged[A] = a×false def flatMap[A, B](f: A => Flagged[B])(fa: Flagged[A]): Flagged[B] = val (a×x1) = fa val (b×x2) = f(a) val x3 = x1 || x2 b×x3 ``` --- ## Breaking flagged data .center[] .diff-rm[ ```scala *type Flagged[A] = `A×`Boolean def pure[A](a: A): Flagged[A] = * `a×`false def flatMap[A, B](f: A => Flagged[B])(fa: Flagged[A]): Flagged[B] = * val (`a×`x1) = fa * val (`b×`x2) = f(a) val x3 = x1 || x2 * `b×`x3 ``` ] --- ## Breaking flagged data .center[] .diff-rm[ ```scala *type Flagged[A] = `A×`Boolean def pure[A](a: A): Flagged[A] = * `a×`false * *`def flatMap[A, B](f: A => Flagged[B])(fa: Flagged[A]): Flagged[B] =` * `val (a×x1) = fa` * `val (b×x2) = f(a)` * `val x3 = x1 || x2` * * `b×`x3 ``` ] --- ## Breaking flagged data .center[] ```scala type Flagged[A] = Boolean def pure[A](a: A): Flagged[A] = false ``` --- ## Breaking flagged data .center[] ```scala type Flagged[A] = Boolean def pure[A](a: A): Flagged[A] = false ``` --- ## Breaking flagged data .center[] .diff-rm[ ```scala *type `Flagged`[A] = Boolean *def pure[A](a: A): `Flagged`[A] = false ``` ] --- ## Flag .center[] .diff-add[ ```scala *type `Flag`[A] = Boolean *def pure[A](a: A): `Flag`[A] = false ``` ] --- ## Flag .center[] ```scala type Flag[A] = Boolean def pure[A](a: A): Flag[A] = false ``` --- ## Key takeaways A binary parametric product type is: * a `Functor`. * not an `Apply` if the other member is not a `Semigroup`. * not an `Applicative` if the other member is not a `Monoid`. * not a `FlatMap` if an `Apply` with a phantom type. -- * not a `Monad` if an `Applicative` with a phantom type. --- class: center, middle # FlatMap but not Monad --- ## Monad .center[] --- ## Monad .center[] --- ## Flagged data .center[] ```scala type Flagged[A] = A×Boolean def pure[A](a: A): Flagged[A] = a×false def flatMap[A, B](f: A => Flagged[B])(fa: Flagged[A]): Flagged[B] = val (a×x1) = fa val (b×x2) = f(a) val x3 = x1 || x2 b×x3 ``` --- ## Breaking flagged data .center[] .diff-rm[ ```scala *type Flagged[A] = A×`Boolean` def pure[A](a: A): Flagged[A] = a×false def flatMap[A, B](f: A => Flagged[B])(fa: Flagged[A]): Flagged[B] = val (a×x1) = fa val (b×x2) = f(a) val x3 = x1 || x2 b×x3 ``` ] --- ## Breaking flagged data .center[] .diff-add[ ```scala *type Flagged[A] = A×`PosInt` def pure[A](a: A): Flagged[A] = a×false def flatMap[A, B](f: A => Flagged[B])(fa: Flagged[A]): Flagged[B] = val (a×x1) = fa val (b×x2) = f(a) val x3 = x1 || x2 b×x3 ``` ] --- ## Breaking flagged data .center[] .diff-rm[ ```scala type Flagged[A] = A×PosInt def pure[A](a: A): Flagged[A] = a×false def flatMap[A, B](f: A => Flagged[B])(fa: Flagged[A]): Flagged[B] = val (a×x1) = fa val (b×x2) = f(a) * val x3 = `x1 || x2` b×x3 ``` ] --- ## Breaking flagged data .center[] .diff-add[ ```scala type Flagged[A] = A×PosInt def pure[A](a: A): Flagged[A] = a×false def flatMap[A, B](f: A => Flagged[B])(fa: Flagged[A]): Flagged[B] = val (a×x1) = fa val (b×x2) = f(a) * val x3 = `x1 + x2` b×x3 ``` ] --- ## Breaking flagged data .center[] .diff-add[ ```scala type Flagged[A] = A×PosInt def pure[A](a: A): Flagged[A] = a×false def flatMap[A, B](f: A => Flagged[B])(fa: Flagged[A]): Flagged[B] = val (a×x1) = fa val (b×x2) = f(a) val x3 = x1 + x2 b×x3 ``` ] --- ## Breaking flagged data .center[] .diff-add[ ```scala type Flagged[A] = A×PosInt def pure[A](a: A): Flagged[A] = a×false def flatMap[A, B](f: A => Flagged[B])(fa: Flagged[A]): Flagged[B] = val (a×x1) = fa val (b×x2) = f(a) val x3 = x1 + x2 b×x3 ``` ] --- ## Breaking flagged data .center[] .diff-rm[ ```scala type Flagged[A] = A×PosInt def pure[A](a: A): Flagged[A] = * a×`false` def flatMap[A, B](f: A => Flagged[B])(fa: Flagged[A]): Flagged[B] = val (a×x1) = fa val (b×x2) = f(a) val x3 = x1 + x2 b×x3 ``` ] --- ## Breaking flagged data .center[] .diff-rm[ ```scala type Flagged[A] = A×PosInt * *`def pure[A](a: A): Flagged[A] =` * `a×false` def flatMap[A, B](f: A => Flagged[B])(fa: Flagged[A]): Flagged[B] = val (a×x1) = fa val (b×x2) = f(a) val x3 = x1 + x2 b×x3 ``` ] --- ## Breaking flagged data .center[] ```scala type Flagged[A] = A×PosInt def flatMap[A, B](f: A => Flagged[B])(fa: Flagged[A]): Flagged[B] = val (a×x1) = fa val (b×x2) = f(a) val x3 = x1 + x2 b×x3 ``` --- ## Breaking flagged data .center[] .diff-rm[ ```scala *type `Flagged`[A] = A×PosInt *def flatMap[A, B](f: A => `Flagged`[B])(fa: `Flagged`[A])`: Flagged[B]` = val (a×x1) = fa val (b×x2) = f(a) val x3 = x1 + x2 b×x3 ``` ] --- ## Weighted data .center[] .diff-add[ ```scala *type `Weighted`[A] = A×PosInt *def flatMap[A, B](f: A => `Weighted`[B])(fa: `Weighted`[A]) = val (a×x1) = fa val (b×x2) = f(a) val x3 = x1 + x2 b×x3 ``` ] --- ## Weighted data .center[] ```scala type Weighted[A] = A×PosInt def flatMap[A, B](f: A => Weighted[B])(fa: Weighted[A]) = val (a×x1) = fa val (b×x2) = f(a) val x3 = x1 + x2 b×x3 ``` --- ## Key takeaways A binary parametric product type is: * a `Functor`. * not an `Apply` if the other member is not a `Semigroup`. * not an `Applicative` if the other member is not a `Monoid`. * not a `FlatMap` if an `Apply` with a phantom type. * not a `Monad` if an `Applicative` with a phantom type. -- * not a `Monad` if a `FlatMap` and the other member is not a `Monoid`. ??? And with this, I've shown you all the counter examples I wanted to show you. To summarize, we've seen, roughly, that: * contravariant functors were not functors. * apply and applicative have a deep connection with semigroups and monoids - which is not exactly new, this was front and center in McBride's paper on applicative functors, but re-discovering this connection through counter example made it feel more concrete to me. * monads and flatmaps are about having a value - which sounds a little weird on the face of it, but really is just another way of saying that monads are about chaining functions. I hope this was, if not new information, at least an entertaining twist on old information. I certainly was quite entertaining to work on and figure out for myself. And with that, I'd be happy to answer any question you might have. --- class: center, middle name: questions [
][Slides] [Nicolas Rinaudo] • [@NicolasRinaudo@functional.cafe] [@NicolasRinaudo@functional.cafe]:https://functional.cafe/@NicolasRinaudo [Nicolas Rinaudo]:https://nrinaudo.github.io/ [Slides]:https://nrinaudo.github.io/things-that-are-things/