Skip to content

Instantly share code, notes, and snippets.

@jdegoes
Created August 21, 2016 16:24
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save jdegoes/63786ba167ec3da151c0d079a5516ae1 to your computer and use it in GitHub Desktop.
Save jdegoes/63786ba167ec3da151c0d079a5516ae1 to your computer and use it in GitHub Desktop.
A pedagogical free(er) monad in 23 lines of Scala
sealed trait Free[F[_], A] { self =>
final def map[B](ab: A => B): Free[F, B] = Free.flatMap(self, ab andThen (Free.point[F, B](_)))
final def flatMap[B](afb: A => Free[F, B]): Free[F, B] = Free.flatMap(self, afb)
final def interpret[G[_]: Monad](fg: F ~> G): G[A] = self match {
case Free.Point(a0) => a0().point[G]
case Free.Effect(fa) => fg(fa)
case fm : Free.FlatMap[F, A] =>
val ga0 = fm.fa.interpret[G](fg)
ga0.flatMap(a0 => fm.afb(a0).interpret[G](fg))
}
}
object Free {
def point[F[_], A](a: => A): Free[F, A] = Point[F, A](() => a)
def liftF[F[_], A](fa: F[A]): Free[F, A] = Effect[F, A](fa)
private final case class Point[F[_], A](a0: () => A) extends Free[F, A]
private final case class Effect[F[_], A](fa: F[A]) extends Free[F, A]
private sealed trait FlatMap[F[_], B] extends Free[F, B] {
type A; def fa: Free[F, A]; def afb: A => Free[F, B]
}
private def flatMap[F[_], A0, B](fa0: Free[F, A0], afb0: A0 => Free[F, B]): FlatMap[F, B] = new FlatMap[F, B] {
type A = A0; def fa = fa0; def afb = afb0
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment