Skip to content

Instantly share code, notes, and snippets.

@gbersac
Created February 24, 2017 16:09
Show Gist options
  • Save gbersac/f5ac8913b05418a13998319824a7d5a7 to your computer and use it in GitHub Desktop.
Save gbersac/f5ac8913b05418a13998319824a7d5a7 to your computer and use it in GitHub Desktop.
trait Functor[F[_]] {
def map[A,B](fa: F[A])(f: A => B): F[B]
def distribute[A,B](fab: F[(A, B)]): (F[A], F[B]) =
(map(fab)(_._1), map(fab)(_._2))
def codistribute[A,B](e: Either[F[A], F[B]]): F[Either[A, B]] = e match {
case Left(fa) => map(fa)(Left(_))
case Right(fb) => map(fb)(Right(_))
}
}
object Functor {
val listFunctor = new Functor[List] {
def map[A,B](as: List[A])(f: A => B): List[B] = as map f
}
}
trait Monad[F[_]] extends Functor[F] {
def unit[A](a: => A): F[A]
def flatMap[A,B](ma: F[A])(f: A => F[B]): F[B]
def map[A,B](ma: F[A])(f: A => B): F[B] =
flatMap(ma)(a => unit(f(a)))
def map2[A,B,C](ma: F[A], mb: F[B])(f: (A, B) => C): F[C] =
flatMap(ma)(a => map(mb)(b => f(a, b)))
def sequence[A](lma: List[F[A]]): F[List[A]] =
lma.foldRight(unit(List[A]()))((ma, mla) => map2(ma, mla)(_ :: _))
def traverse[A,B](la: List[A])(f: A => F[B]): F[List[B]] =
la.foldRight(unit(List[B]()))((a, mlb) => map2(f(a), mlb)(_ :: _))
def join[A](mma: F[F[A]]): F[A] = flatMap(mma)(ma => ma)
}
sealed trait Free[F[_],A] {
def flatMap[B](f: A => Free[F,B]): Free[F,B] =
FlatMap(this, f)
def map[B](f: A => B): Free[F,B] =
flatMap(f andThen (Return(_)))
}
case class Return[F[_],A](a: A) extends Free[F,A]
case class Suspend[F[_],A](s: F[A]) extends Free[F,A]
case class FlatMap[F[_],A,B](s: Free[F,A], f: A => Free[F,B]) extends Free[F,B]
def freeMonad[F[_]]: Monad[({type f[a] = Free[F,a]})#f] =
new Monad[({type f[a] = Free[F,a]})#f] {
def unit[A](a: => A) = Return(a)
def flatMap[A,B](fa: Free[F, A])(f: A => Free[F, B]) = fa flatMap f
}
@annotation.tailrec
def runTrampoline[A](a: Free[Function0,A]): A = a match {
case Return(ra) => ra
case Suspend(sa) => sa()
case FlatMap(fma, f) => fma match {
case Return(ra) => runTrampoline(f(ra))
case Suspend(sa) => runTrampoline(f(sa))
case FlatMap(fma2, f2) => runTrampoline(fma2 flatMap { na => f2(na) flatMap f })
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment