Skip to content

Instantly share code, notes, and snippets.

@ukitaka
Created July 4, 2017 07:51
Show Gist options
  • Save ukitaka/ad45cbca1f589d8e2fa1269892b24e5c to your computer and use it in GitHub Desktop.
Save ukitaka/ad45cbca1f589d8e2fa1269892b24e5c to your computer and use it in GitHub Desktop.
OptionT
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
trait Monad[F[_]] extends Functor[F] {
def apply[A](a: A): F[A]
def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
def map[A, B](fa: F[A])(f: A => B): F[B] = flatMap(fa)(a => apply(f(a)))
}
case class Id[A](a: A)
implicit val idMonad = new Monad[Id] {
def apply[A](a: A): Id[A] = Id(a)
def flatMap[A, B](fa: Id[A])(f: A => Id[B]): Id[B] = f(fa.a)
}
implicit val optionMonad = new Monad[Option] {
def apply[A](a: A): Option[A] = Some(a)
def flatMap[A, B](fa: Option[A])(f: A => Option[B]): Option[B] = fa.flatMap(f)
}
// Fについて、FunctorやMonadがあれば合成できる
final case class OptionT[F[_], A](value: F[Option[A]]) {
def map[B](f: A => B)(implicit F: Functor[F]): F[Option[B]] =
F.map(value)(_.map(f))
def apply[A](a: A)(implicit M: Monad[F]): OptionT[F, A] = OptionT(M.apply(Some(a)))
def flatMap[B](f: A => F[Option[B]])(implicit M: Monad[F]): F[Option[B]] =
M.flatMap(value) {
case Some(a) => f(a)
case None => M.apply(None)
}
}
// OptionT自体をFunctor/Monadにする
implicit def optionTFunctor[F[_]](implicit F: Functor[F]) = new Functor[({ type R[A] = OptionT[F, A] })#R] {
def map[A, B](fa: OptionT[F, A])(f: A => B): OptionT[F, B] = OptionT(fa.map(f))
}
implicit def optionTMonad[F[_]](implicit M: Monad[F]) = new Monad[({ type R[A] = OptionT[F, A] })#R] {
def apply[A](a: A): OptionT[F, A] = OptionT(M.apply(Some(a)))
def flatMap[A, B](fa: OptionT[F, A])(f: A => OptionT[F, B]): OptionT[F, B] = {
OptionT(fa.flatMap { a => f(a).value })
}
}
val c = for {
a <- OptionT[Id, Int](Id(Some(1)))
b <- OptionT[Id, Int](Id(Some(2)))
} yield a + b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment