Last active
August 29, 2015 14:04
-
-
Save bhuemer/b31e4ca21d2d0ea9e363 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package at.bhuemer.fpis.chapter11 | |
import scala.concurrent.{Await, ExecutionContext, Future} | |
/** | |
* | |
*/ | |
trait Monad[F[_]] { self => | |
// The primitives that any monad implementation must provide | |
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))) | |
// Ex. 3: | |
def sequence[A](mas: List[F[A]]): F[List[A]] = | |
mas match { | |
case Nil => unit(Nil) | |
case head :: tail => for { | |
a <- head | |
as <- sequence(tail) | |
} yield a :: as | |
} | |
def traverse[A, B](la: List[A])(f: A => F[B]): F[List[B]] = | |
sequence(la map f) | |
// Ex. 4/5: | |
def replicateM[A](n: Int, ma: F[A]): F[List[A]] = | |
sequence(List.fill(n)(ma)) | |
// Ex. 6 | |
def filterM[A](as: List[A])(f: A => F[Boolean]): F[List[A]] = | |
as match { | |
case Nil => unit(Nil) | |
case head :: tail => for { | |
keep <- f(head) | |
filtered <- filterM(tail)(f) | |
} yield { | |
if (keep) head :: filtered | |
else filtered | |
} | |
} | |
// The more complicated version I had in the beginning | |
def filterF[A](as: List[A])(f: A => F[Boolean]): F[List[A]] = { | |
// This first step triggers the calculation for each element in the list and zips it together in a tuple with the element itself .. | |
val evaluationTriggered: List[F[(A, Boolean)]] = as map { a => f(a) map { (a, _) } } | |
// .. now we need to wait for results by sequencing the whole thing together .. | |
val evaluationSequenced: F[List[(A, Boolean)]] = sequence(evaluationTriggered) | |
// .. and finally we evaluate those results. | |
val evaluationFiltered: F[List[A]] = evaluationSequenced map { list => list.filter(_._2).map(_._1) } | |
evaluationFiltered | |
} | |
// Ex. 7 | |
def compose[A, B, C](fa: A => F[B], fb: B => F[C]): A => F[C] = fa(_) flatMap fb | |
def flatMapC[A, B](ma: F[A])(f: A => F[B]): F[B] = compose[F[A], A, B](identity, f)(ma) | |
// Ex. 9? | |
def flatten[A](mma: F[F[A]]): F[A] = join(mma) | |
def join[A](mma: F[F[A]]): F[A] = | |
mma flatMap identity | |
def composeJ[A, B, C](fa: A => F[B], fb: B => F[C]): A => F[C] = a => flatten(fa(a) map fb) | |
def flatMapJ[A, B](ma: F[A])(f: A => F[B]): F[B] = flatten(ma map f) | |
// Utilities: | |
implicit class MonadOps[A](ma: F[A]) { | |
def flatMap[B](f: A => F[B]): F[B] = self.flatMap(ma)(f) | |
def map[B](f: A => B): F[B] = self.map(ma)(f) | |
} | |
} | |
// Ex. 1 (parts of it) | |
object OptionMonad extends Monad[Option] { | |
override def unit[A](a: A): Option[A] = Some(a) | |
override def flatMap[A, B](ma: Option[A])(f: A => Option[B]): Option[B] = ma flatMap f | |
override def map[A, B](ma: Option[A])(f: A => B): Option[B] = ma map f | |
} | |
object ListMonad extends Monad[List] { | |
override def unit[A](a: A): List[A] = List(a) | |
override def flatMap[A, B](ma: List[A])(f: A => List[B]): List[B] = ma flatMap f | |
override def map[A, B](ma: List[A])(f: A => B): List[B] = ma map f | |
} | |
class FutureMonad(implicit ex: ExecutionContext) extends Monad[Future] { | |
override def unit[A](a: A): Future[A] = Future { a } | |
override def flatMap[A, B](ma: Future[A])(f: A => Future[B]): Future[B] = ma flatMap f | |
override def map[A, B](ma: Future[A])(f: A => B): Future[B] = ma map f | |
} | |
object FutureMonadApp { | |
import ExecutionContext.Implicits.global | |
import scala.concurrent.duration._ | |
def slowGreaterThanFilter(threshold: Int)(value: Int): Future[Boolean] = Future { | |
println(s"Checking $value") | |
Thread.sleep(100) | |
value > threshold | |
} | |
def main(args: Array[String]): Unit = { | |
val futureMonad = new FutureMonad() | |
val values = (1 to 100).toList | |
val filteredM = futureMonad.filterM(values)(slowGreaterThanFilter(50)) | |
println("filteredM: " + Await.result(filteredM, 10 minutes)) | |
val filteredF = futureMonad.filterF(values)(slowGreaterThanFilter(50)) | |
println("filteredF: " + Await.result(filteredF, 10 minutes)) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment