Why The Free Monad isn't Free
-
Upload
kelley-robinson -
Category
Engineering
-
view
9.640 -
download
1
Transcript of Why The Free Monad isn't Free
![Page 1: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/1.jpg)
![Page 2: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/2.jpg)
[error] Exception encountered [error] java.lang.StackOverflowError
![Page 3: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/3.jpg)
WHY THE FREE MONAD ISN’T FREE
“Let’s just trampoline it and add the Free Monad”
@kelleyrobinson
![Page 4: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/4.jpg)
WHY THE FREE MONAD ISN’T FREE
@kelleyrobinson
“Let’s just trampoline it and add the Free Monad”
![Page 5: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/5.jpg)
![Page 6: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/6.jpg)
Why The Free Monad Isn’t Free
Kelley Robinson Data & Infrastructure Engineer
Sharethrough
@kelleyrobinson
![Page 7: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/7.jpg)
WHY THE FREE MONAD ISN’T FREE
- Monoids, Functors & Monads
- How to be “Free”
- Why & Why Not “Free”
- Alternatives
- Real World Applications
$
@kelleyrobinson
![Page 8: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/8.jpg)
WHY THE FREE MONAD ISN’T FREE
github.com/robinske/monad-examples
![Page 9: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/9.jpg)
WHY THE FREE MONAD ISN’T FREE
https://twitter.com/rickasaurus/status/705134684427128833
![Page 10: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/10.jpg)
WHY THE FREE MONAD ISN’T FREE
Monoids
@kelleyrobinson
![Page 11: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/11.jpg)
@kelleyrobinson
trait Monoid[A] { def append(a: A, b: A): A def identity: A
}
![Page 12: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/12.jpg)
WHY THE FREE MONAD ISN'T FREE
Monoids
Image credit: deluxebattery.com
![Page 13: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/13.jpg)
WHY THE FREE MONAD ISN'T FREE
Properties
Identity: "no-op" value
Associativity: grouping doesn't matter
@kelleyrobinson
![Page 14: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/14.jpg)
@kelleyrobinson
object StringConcat extends Monoid[String] { def append(a: String, b: String): String = a + b def identity: String = "" }
![Page 15: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/15.jpg)
@kelleyrobinson
object IntegerAddition extends Monoid[Int] { def append(a: Int, b: Int): Int = a + b def identity: Int = 0 }
![Page 16: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/16.jpg)
@kelleyrobinson
object IntegerMultiplication extends Monoid[Int] { def append(a: Int, b: Int): Int = a * b def identity: Int = 1 }
![Page 17: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/17.jpg)
@kelleyrobinson
object FunctionComposition /* extends Monoid[_=>_] */ { def append[A, B, C](f1: A => B, f2: B => C): A => C = (a: A) => f2(f1(a)) def identity[A]: A => A = (a: A) => a }
![Page 18: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/18.jpg)
@kelleyrobinson
object FunctionComposition /* extends Monoid[_=>_] */ { def append[A, B, C](f1: A => B, f2: B => C): A => C = (a: A) => f2(f1(a)) def identity[A]: A => A = (a: A) => a }
![Page 19: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/19.jpg)
WHY THE FREE MONAD ISN’T FREE
Functors
@kelleyrobinson
![Page 20: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/20.jpg)
@kelleyrobinson
trait Functor[F[_]] { def map[A, B](a: F[A])(fn: A => B): F[B] }
![Page 21: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/21.jpg)
WHY THE FREE MONAD ISN'T FREE
@kelleyrobinson
Properties
Identity: "no-op" value
Composition: grouping doesn't matter
![Page 22: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/22.jpg)
@kelleyrobinson
sealed trait Option[+A] case class Some[A](a: A) extends Option[A] case object None extends Option[Nothing]
object OptionFunctor extends Functor[Option] { def map[A, B](a: Option[A])(fn: A => B): Option[B] = a match { case Some(something) => Some(fn(something)) case None => None }}
![Page 23: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/23.jpg)
@kelleyrobinson
it("should follow the identity law") {
def identity[A](a: A): A = a
assert(map(Some("foo"))(identity) == Some("foo"))
}
![Page 24: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/24.jpg)
@kelleyrobinson
it("should follow the composition law") {
val f: String => String = s => s + "a" val g: String => String = s => s + "l" val h: String => String = s => s + "a"
assert( map(Some("sc"))(f andThen g andThen h) == map(map(map(Some("sc"))(f))(g))(h) == "scala" ) }
![Page 25: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/25.jpg)
Functors are Endofunctors** **in Scala
WHY THE FREE MONAD ISN’T FREE
@kelleyrobinson
![Page 26: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/26.jpg)
WHY THE FREE MONAD ISN’T FREE
Monads
@kelleyrobinson
![Page 27: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/27.jpg)
"The term monad is a bit vacuous if you are not a
mathematician. An alternative term is computation builder."
WHY THE FREE MONAD ISN’T FREE
@kelleyrobinson http://stackoverflow.com/questions/44965/what-is-a-monad
![Page 28: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/28.jpg)
@kelleyrobinson
trait Monad[M[_]] { def pure[A](a: A): M[A]
def flatMap[A, B](a: M[A])(fn: A => M[B]): M[B]
}
![Page 29: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/29.jpg)
@kelleyrobinson
sealed trait Option[+A] case class Some[A](a: A) extends Option[A] case object None extends Option[Nothing]object OptionMonad extends Monad[Option] { def pure[A](a: A): Option[A] = Some(a) def flatMap[A, B](a: Option[A])(fn: A => Option[B]): Option[B] = a match { case Some(something) => fn(something) case None => None }}
![Page 30: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/30.jpg)
@kelleyrobinson
trait Monad[M[_]] { def pure[A](a: A): M[A]
def flatMap[A, B](a: M[A])(fn: A => M[B]): M[B]
}
![Page 31: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/31.jpg)
@kelleyrobinson
trait Monad[M[_]] { def pure[A](a: A): M[A] def flatMap[A, B](a: M[A])(fn: A => M[B]): M[B] def map[A, B](a: M[A])(fn: A => B): M[B] = { flatMap(a){ b: A => pure(fn(b)) } } }
![Page 32: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/32.jpg)
@kelleyrobinson
trait Monad[M[_]] {
def pure[A](a: A): M[A]
def flatMap[A, B](a: M[A])(fn: A => M[B]): M[B] def map[A, B](a: M[A])(fn: A => B): M[B] = { flatMap(a){ b: A => pure(fn(b)) } }
}
![Page 33: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/33.jpg)
@kelleyrobinson
trait Monad[M[_]] { def flatMap[A, B](a: M[A])(fn: A => M[B]): M[B] def append[A, B, C] (f1: A => M[B], f2: B => M[C]): A => M[C] = { a: A => val bs: M[B] = f1(a) val cs: M[C] = flatMap(bs) { b: B => f2(b) } cs } }
![Page 34: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/34.jpg)
@kelleyrobinson
trait Monad[M[_]] { def flatMap[A, B](a: M[A])(fn: A => M[B]): M[B] def append[A, B, C] (f1: A => M[B], f2: B => M[C]): A => M[C] = { a: A => val bs: M[B] = f1(a) val cs: M[C] = flatMap(bs) { b: B => f2(b) } cs } }
![Page 35: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/35.jpg)
WHY THE FREE MONAD ISN'T FREE
Properties
Identity: "no-op" value
Composition: grouping doesn't matter
@kelleyrobinson
![Page 36: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/36.jpg)
WHY THE FREE MONAD ISN'T FREE
Compose functions for values in a context
Think: Lists, Options, Futures
@kelleyrobinson
![Page 37: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/37.jpg)
trait Monad[M[_]] extends Functor[M] /* with Monoid[_=>M[_]] */ { def pure[A](a: A): M[A]
def flatMap[A, B](a: M[A])(fn: A => M[B]): M[B] def map[A, B](a: M[A])(fn: A => B): M[B]
def append[A, B, C](f1: A => M[B], f2: B => M[C]): A => M[C]
def identity[A]: A => M[A]
}
@kelleyrobinson
trait Monad[M[_]] extends Functor[M] /* with Monoid[ _ => M[_] ] */ { def pure[A](a: A): M[A]
def flatMap[A, B](a: M[A])(fn: A => M[B]): M[B] def map[A, B](a: M[A])(fn: A => B): M[B]
def append[A, B, C](f1: A => M[B], f2: B => M[C]): A => M[C]
def identity[A]: A => M[A]
}
![Page 38: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/38.jpg)
@kelleyrobinson
trait Monad[M[_]] extends Functor[M] /* with Monoid[ _ => M[_] ] */ { def pure[A](a: A): M[A]
def flatMap[A, B](a: M[A])(fn: A => M[B]): M[B] def map[A, B](a: M[A])(fn: A => B): M[B]
def append[A, B, C](f1: A => M[B], f2: B => M[C]): A => M[C]
def identity[A]: A => M[A]
}
![Page 39: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/39.jpg)
@kelleyrobinson
object FunctionComposition /* extends Monoid[_ => _] */{ ...
}
trait Monad[M[_]] /* extends Monoid[_ => M[_]] */{ ...
}
![Page 40: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/40.jpg)
WHY THE FREE MONAD ISN’T FREE
- Monoids, Functors & Monads
- How to be “Free”
- Why & Why Not “Free”
- Alternatives
- Real World Applications
$
@kelleyrobinson
![Page 41: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/41.jpg)
WHY THE FREE MONAD ISN'T FREE
The word "free" is used in the sense of "unrestricted" rather than "zero-cost"
$
@kelleyrobinson
![Page 42: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/42.jpg)
WHY THE FREE MONAD ISN'T FREE
"Freedom not beer"
https://en.wikipedia.org/wiki/Gratis_versus_libre#/media/File:Galuel_RMS_-_free_as_free_speech,_not_as_free_beer.png
![Page 43: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/43.jpg)
WHY THE FREE MONAD ISN’T FREE
Free Monoids
@kelleyrobinson
![Page 44: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/44.jpg)
@kelleyrobinson
trait Monoid[A] { def append(a: A, b: A): A
def identity: A
}
![Page 45: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/45.jpg)
WHY THE FREE MONAD ISN’T FREE
Free Monoids • Free from interpretation
• No lost input data when
appending
@kelleyrobinson
image credit: http://celestemorris.com
![Page 46: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/46.jpg)
@kelleyrobinson
// I'm free!
class ListConcat[A] extends Monoid[List[A]] {
def append(a: List[A], b: List[A]): List[A] = a ++ b
def identity: List[A] = List.empty[A]
}
![Page 47: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/47.jpg)
@kelleyrobinson
// I'm not free :(
object IntegerAddition extends Monoid[Int] { def append(a: Int, b: Int): Int = a + b def identity: Int = 0 }
![Page 48: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/48.jpg)
WHY THE FREE MONAD ISN’T FREE
Free Monads
@kelleyrobinson
![Page 49: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/49.jpg)
Don't lose any data! (that means no evaluating functions)
WHY THE FREE MONAD ISN’T FREE
@kelleyrobinson
![Page 50: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/50.jpg)
@kelleyrobinson
def notFreeAppend[A, B, C] (f1: A => M[B], f2: B => M[C]): A => M[C] = {
a: A => // evaluate f1 val bs: M[B] = f1(a) // evaluate f2 val cs: M[C] = flatMap(bs) { b: B => f2(b) } cs }
![Page 51: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/51.jpg)
@kelleyrobinson
sealed trait Free[F[_], A] { self =>
}
![Page 52: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/52.jpg)
@kelleyrobinson
sealed trait Free[F[_], A] { self =>
} case class Return[F[_], A](given: A) extends Free[F, A]
![Page 53: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/53.jpg)
@kelleyrobinson
sealed trait Free[F[_], A] { self =>
} case class Return[F[_], A](given: A) extends Free[F, A]
case class Suspend[F[_], A](fn: F[A]) extends Free[F, A]
![Page 54: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/54.jpg)
@kelleyrobinson
sealed trait Free[F[_], A] { self =>
} case class Return[F[_], A](given: A) extends Free[F, A]
case class Suspend[F[_], A](fn: F[A]) extends Free[F, A]
case class FlatMap[F[_], A, B] (free: Free[F, A], fn: A => Free[F, B]) extends Free[F, B]
![Page 55: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/55.jpg)
@kelleyrobinson
sealed trait Free[F[_], A] { self => def flatMap ... def pure ... def map ... } case class Return[F[_], A](given: A) extends Free[F, A]
case class Suspend[F[_], A](fn: F[A]) extends Free[F, A]
case class FlatMap[F[_], A, B] (free: Free[F, A], fn: A => Free[F, B]) extends Free[F, B]
![Page 56: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/56.jpg)
@kelleyrobinson
sealed trait Todo[A] case class NewTask[A](task: A) extends Todo[A] case class CompleteTask[A](task: A) extends Todo[A] case class GetTasks[A](default: A) extends Todo[A]
def newTask[A](task: A): Free[Todo, A] = Suspend(NewTask(task))
def completeTask[A](task: A): Free[Todo, A] = Suspend(CompleteTask(task))
def getTasks[A](default: A): Free[Todo, A] = Suspend(GetTasks(default))
![Page 57: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/57.jpg)
@kelleyrobinson
val todos: Free[Todo, Map[String, Boolean]] = for { _ <- newTask("Go to scala days") _ <- newTask("Write a novel") _ <- newTask("Meet Tina Fey") _ <- completeTask("Go to scala days") tsks <- getTasks(Map.empty) } yield tsks
![Page 58: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/58.jpg)
@kelleyrobinson
val todosExpanded: Free[Todo, Map[String, Boolean]] = FlatMap( Suspend(NewTask("Go to scala days")), (a: String) => FlatMap( Suspend(NewTask("Write a novel")), (b: String) => FlatMap( Suspend(NewTask("Meet Tina Fey")), (c: String) => FlatMap( Suspend(CompleteTask("Go to scala days")), (d: String) => Suspend(GetTasks(default = Map.empty)) ) ) ) )
![Page 59: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/59.jpg)
WHY THE FREE MONAD ISN’T FREE
- Monoids, Functors & Monads
- How to be “Free”
- Why & Why Not “Free”
- Alternatives
- Real World Applications
$
@kelleyrobinson
![Page 60: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/60.jpg)
WHY THE FREE MONAD ISN'T FREE
What's the point?
• Defer side effects
• Multiple interpreters
• Stack safety
@kelleyrobinson
![Page 61: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/61.jpg)
@kelleyrobinson
(1 to 1000).flatMap { i => doSomething(i).flatMap { j => doSomethingElse(j).flatMap { k => doAnotherThing(k).map { l => ...
![Page 62: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/62.jpg)
WHY THE FREE MONAD ISN’T FREE
“Let’s just trampoline it and add the Free Monad”
@kelleyrobinson
![Page 63: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/63.jpg)
WHY THE FREE MONAD ISN’T FREE
Trampolining Express it in a loop
@kelleyrobinson
![Page 64: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/64.jpg)
WHY THE FREE MONAD ISN’T FREE
The Free Monad uses heap instead of using stack.
@kelleyrobinson
![Page 65: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/65.jpg)
@kelleyrobinson
val todosExpanded: Free[Todo, Map[String, Boolean]] = FlatMap( Suspend(NewTask("Go to scala days")), (a: String) => FlatMap( Suspend(NewTask("Write a novel")), (b: String) => FlatMap( Suspend(NewTask("Meet Tina Fey")), (c: String) => FlatMap( Suspend(CompleteTask("Go to scala days")), (d: String) => Suspend(GetTasks(default = Map.empty)) ) ) ) )
![Page 66: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/66.jpg)
WHY THE FREE MONAD ISN’T FREE
@kelleyrobinson
Evaluating Use a loop
![Page 67: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/67.jpg)
@kelleyrobinson
def runFree[F[_], G[_], A]
(f: Free[F, A])
(transform: FunctorTransformer[F, G])
(implicit G: Monad[G]): G[A]
![Page 68: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/68.jpg)
@kelleyrobinson
def runFree[F[_], G[_], A] (f: Free[F, A]) (transform: FunctorTransformer[F, G]) (implicit G: Monad[G]): G[A]
Turn F into G - AKA "Natural Transformation"Input
`G` must be a monad so we can flatMap
![Page 69: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/69.jpg)
@kelleyrobinson
// or 'NaturalTransformation'trait FunctorTransformer[F[_], G[_]] { def apply[A](f: F[A]): G[A] }
// Common symbolic operator type ~>[F[_], G[_]] = FunctorTransformer[F, G]
![Page 70: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/70.jpg)
@kelleyrobinson
/* Function body */
@annotation.tailrec def tailThis(free: Free[F, A]): Free[F, A] = free match { case FlatMap(FlatMap(fr, fn1), fn2) => ... case FlatMap(Return(a), fn) => ... case _ => ... } tailThis(f) match { case Return(a) => ... case Suspend(fa) => ... case FlatMap(Suspend(fa), fn) => ... case _ => ... }
https://github.com/robinske/monad-examples
![Page 71: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/71.jpg)
@kelleyrobinson
tailThis(f) match { case Return(a) => ... case Suspend(fa) => transform(fa) case FlatMap(Suspend(fa), fn) => monad.flatMap(transform(fa))(a => runFree(fn(a))(transform)) case _ => ... }
https://github.com/robinske/monad-examples
![Page 72: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/72.jpg)
@kelleyrobinson
def runLoop[F[_], G[_], A](...): G[A] = { var eval: Free[F, A] = f while (true) { eval match { case Return(a) => ... case Suspend(fa) => ... case FlatMap(Suspend(fa), fn) => ... case FlatMap(FlatMap(given, fn1), fn2) => ... case FlatMap(Return(s), fn) => ... } } throw new AssertionError("Unreachable") }
![Page 73: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/73.jpg)
WHY THE FREE MONAD ISN’T FREE
@kelleyrobinson
Evaluating
Applies transformation on `Suspend`
Trampolining for stack safety
![Page 74: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/74.jpg)
@kelleyrobinson
// or 'NaturalTransformation'trait FunctorTransformer[F[_], G[_]] { def apply[A](f: F[A]): G[A] }
// Common symbolic operator type ~>[F[_], G[_]] = FunctorTransformer[F, G]
![Page 75: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/75.jpg)
@kelleyrobinson
type Id[A] = A
case class TestEvaluator(var model: Map[String, Boolean]) extends FunctorTransformer[Todo, Id] { def apply[A](a: Todo[A]): Id[A]
}
![Page 76: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/76.jpg)
@kelleyrobinson
a match { case NewTask(task) => model = model + (task.toString -> false) task case CompleteTask(task) => model = model + (task.toString -> true) task case GetTasks(default) => model.asInstanceOf[A] }
![Page 77: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/77.jpg)
@kelleyrobinson
it("should evaluate todos") { val result = runFree(todos)(TestEvaluator(Map.empty)) val expected: Map[String, Boolean] = Map( "Go to scala days" -> true, "Write a novel" -> false, "Meet Tina Fey" -> false ) result shouldBe expected}
![Page 78: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/78.jpg)
@kelleyrobinson
case object ActionTestEvaluator extends FunctorTransformer[Todo, Id] { var actions: List[Todo[String]] = List.empty def apply[A](a: Todo[A]): Id[A]
}
![Page 79: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/79.jpg)
@kelleyrobinson
a match { case NewTask(task) => actions = actions :+ NewTask(task.toString) task case CompleteTask(task) => actions = actions :+ CompleteTask(task.toString) task case GetTasks(default) => actions = actions :+ GetTasks("") default }
![Page 80: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/80.jpg)
@kelleyrobinson
it("should evaluate todos actions in order") { runFree(todos)(ActionTestEvaluator) val expected: List[Todo[String]] = List( NewTask("Go to scala days"), NewTask("Write a novel"), NewTask("Meet Tina Fey"), CompleteTask("Go to scala days"), GetTasks("") ) ActionTestEvaluator.actions shouldBe expected }
![Page 81: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/81.jpg)
WHY THE FREE MONAD ISN’T FREE
@kelleyrobinson
Defining multiple interpreters allows you to test side-effecting code without
using testing mocks.
![Page 82: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/82.jpg)
@kelleyrobinson
// Production Interpreter
def apply[A](a: Todo[A]): Option[A] = { a match { case NewTask(task) => /** * Some if DB write succeeds * None if DB write fails * */ case CompleteTask(task) => ... case GetTasks(default) => ... } }
![Page 83: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/83.jpg)
WHY THE FREE MONAD ISN’T FREE
@kelleyrobinson
Justifications
• Defer side effects
• Multiple interpreters
• Stack safety
![Page 84: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/84.jpg)
WHY THE FREE MONAD ISN'T FREE
#BlueSkyScala The path to learning is broken
@kelleyrobinson
Credit: Jessica Kerr
![Page 85: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/85.jpg)
WHY THE FREE MONAD ISN'T FREE
Freedom isn't free Reasons to avoid the Free Monad
• Boilerplate • Learning curve • Alternatives
@kelleyrobinson
Credit: Jessica Kerr
![Page 86: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/86.jpg)
WHY THE FREE MONAD ISN’T FREE
- Monoids, Functors & Monads
- How to be “Free”
- Why & Why Not “Free”
- Alternatives
- Real World Applications
$
@kelleyrobinson
![Page 87: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/87.jpg)
WHY THE FREE MONAD ISN’T FREE
@kelleyrobinson
Know your domain
![Page 88: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/88.jpg)
WHY THE FREE MONAD ISN'T FREE
Functional Spectrum Where does your team fall?
Java Haskell
![Page 89: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/89.jpg)
WHY THE FREE MONAD ISN'T FREE
Functional Spectrum Where does your team fall?
Java Haskell
![Page 90: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/90.jpg)
WHY THE FREE MONAD ISN’T FREE
@kelleyrobinson
Alternatives for maintaining stack safety
![Page 91: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/91.jpg)
@kelleyrobinson
final override def map[B, That](f: A => B) (implicit bf: CanBuildFrom[List[A], B, That]): That = { if (bf eq List.ReusableCBF) { if (this eq Nil) Nil.asInstanceOf[That] else { val h = new ::[B](f(head), Nil) var t: ::[B] = h var rest = tail while (rest ne Nil) { val nx = new ::(f(rest.head), Nil) t.tl = nx t = nx rest = rest.tail } h.asInstanceOf[That] } } else super.map(f)}
![Page 92: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/92.jpg)
WHY THE FREE MONAD ISN’T FREE
@kelleyrobinson
Alternatives for managing side effects
![Page 93: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/93.jpg)
@kelleyrobinson
import java.sql.ResultSetcase class Person(name: String, age: Int)def getPerson(rs: ResultSet): Person = { val name = rs.getString(1) val age = rs.getInt(2) Person(name, age)}
![Page 94: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/94.jpg)
@kelleyrobinson
def handleFailure[A](f: => A): ActionResult \/ A = { Try(f) match { case Success(res) => res.right case Failure(e) => InternalServerError(reason = e.getMessage).left }} handleFailure(getPerson(rs))
![Page 95: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/95.jpg)
WHY THE FREE MONAD ISN’T FREE
- Monoids, Functors & Monads
- How to be “Free”
- Why & Why Not “Free”
- Alternatives
- Real World Applications
$
@kelleyrobinson
![Page 96: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/96.jpg)
WHY THE FREE MONAD ISN'T FREE
Scalaz Scalaz is a Scala library for functional programming. http://scalaz.github.io/scalaz/
Cats Lightweight, modular, and extensible library for functional programming. http://typelevel.org/cats/
@kelleyrobinsonhttp://www.slideshare.net/jamesskillsmatter/real-world-scalaz
![Page 97: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/97.jpg)
WHY THE FREE MONAD ISN'T FREE
Examples
• Doobie
• scalaz.concurrent.Task
@kelleyrobinson
https://github.com/tpolecat/doobie
![Page 98: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/98.jpg)
@kelleyrobinson
import scalaz.concurrent.Task
def apply(conf: Config, messages: List[SQSMessage]): Unit = { val tasks = messages.map(m => Task { processSQSMessage(conf, m) }) Task.gatherUnordered(tasks).attemptRun match { case -\/(exp) => error(s"Unable to process message") case _ => () }}
![Page 99: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/99.jpg)
@kelleyrobinson
// yikes object Task { implicit val taskInstance: Nondeterminism[Task] with Catchable[Task] with MonadError[({type λ[α,β] = Task[β]})#λ,Throwable] = new Nondeterminism[Task] with Catchable[Task] with MonadError[({type λ[α,β] = Task[β]})#λ,Throwable] { ... } }
![Page 100: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/100.jpg)
WHY THE FREE MONAD ISN’T FREE
@kelleyrobinson
My experience... ...what happened?
![Page 101: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/101.jpg)
WHY THE FREE MONAD ISN’T FREE
- Know your domain
- Use clean abstractions
- Share knowledge
$
@kelleyrobinson
![Page 103: Why The Free Monad isn't Free](https://reader034.fdocuments.net/reader034/viewer/2022051404/58f9ad5d760da3da068b9782/html5/thumbnails/103.jpg)
WHY THE FREE MONAD ISN’T FREE
@kelleyrobinson
Acknowledgements & Resources
Special thanks to: • Sharethrough • Rúnar Bjarnason • Rob Norris • Eugene Yokota • Jessica Kerr • David Hoyt • Danielle Sucher • Charles Ruhland
Resources for learning more about Free Monads: • http://blog.higher-order.com/assets/trampolines.pdf • http://eed3si9n.com/learning-scalaz/ • https://stackoverflow.com/questions/44965/what-is-a-monad • https://byorgey.wordpress.com/2009/01/12/abstraction-intuition-and-the-monad-tutorial-fallacy/ • http://hseeberger.github.io/blog/2010/11/25/introduction-to-category-theory-in-scala/ • https://en.wikipedia.org/wiki/Free_object • https://softwaremill.com/free-monads/ • https://github.com/davidhoyt/kool-aid/ • https://www.youtube.com/watch?v=T4956GI-6Lw
Other links and resources: • https://skillsmatter.com/skillscasts/6483-keynote-scaling-intelligence-moving-ideas-forward • https://stackoverflow.com/questions/7213676/forall-in-scala but that boilerplate