Skip to content

Instantly share code, notes, and snippets.

@johnynek
Created October 7, 2017 01:55
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johnynek/d1556f7ce297ee134095e43b1803a2d3 to your computer and use it in GitHub Desktop.
Save johnynek/d1556f7ce297ee134095e43b1803a2d3 to your computer and use it in GitHub Desktop.
I hadn't realized (maybe the original paper mentioned) but tailRecM + map is enough to do flatMap (and of course logically flatMap + pure are enough to do tailRecM).
object Monad {
trait Monad[F[_]] {
def pure[A](a: A): F[A]
/**
* We can have a default implementation in terms of tailRecM
* and map
*/
def flatMap[A, B](fa: F[A])(fn: A => F[B]): F[B] = {
def step(first: Option[A]): F[Either[Option[A], B]] =
first match {
case None => map(fa) { a => Left(Some(a)) }
case Some(a) => map(fn(a))(Right(_))
}
tailRecM(Option.empty[A])(step(_))
}
def map[A, B](a: F[A])(fn: A => B): F[B] =
flatMap(a)(fn.andThen(pure(_)))
/**
* This is logically correct, but not stack safe unless
* F[_] is trampolined
*/
def tailRecM[A, B](a: A)(fn: A => F[Either[A, B]]): F[B] =
flatMap(fn(a)) {
case Left(a) => tailRecM(a)(fn)
case Right(b) => pure(b)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment