{ 1 + 2 } //happening asynchronously! val f2 = Future { 3 + 5 } ! ! val f3 = for {! a <- f1! b <- f2! } yield ! a + b! ! //we only want to Await at the end of the world ! val result = Await.result(f3, Duration(5, SECONDS))! ! println(result)! }!
we return none! def doPipeLine(i: Int): Option[String] = ! pipeline(Option(i))! ! ! // we get parallelism (because of the call to point in the pipeline)! def doPipelineF(i: Int): Future[String] = ! pipeline(Future { i })!
flatMap[B](f: A => OptionHelper[F,B])! ! ! ! ! ! ! (implicit M:FlatMapable[F]): OptionHelper[F,B] = ! ! ! ! ! ! new OptionHelper[F,B](M.flatMap(foa)(o => o match {! case Some(a) => f(a).foa! case None => M.point(None: Option[B])! }))! }! ! ! /*! we are able to access what’s inside the F[Option[A]] no matter what the F is now, as long as there exists an implicit FlatMapable for F!! */ !
But there’s a problem. The type parameter for FlatMapable needs to have a single kind parameter. The types for OptionHelper are [_],A. [_],A doesn’t fit in something that takes just an [_].! !
will have to explain this to all your new team members.! ! implicit def OptionHelperFlatMapable[F[_]](implicit M: FlatMapable[F]): ! ! FlatMapable[({ type l[a] = OptionHelper[F,a]})#l] = ! ! new FlatMapable[({ type l[a] = OptionHelper[F,a]})#l] { ! ! ! def point[A](a: => A): OptionHelper[F,A] = ! ! ! ! new OptionHelper(M.point(Option(a)))! ! !! def flatMap[A,B](fa: OptionHelper[F,A])(f: A => OptionHelper[F,B]):! ! ! ! OptionHelper[F,B] = fa.flatMap(f)! }!
we are doing is declaring ‘on the fly’ a type alias called L that takes a single parameter (a), that is equal to the OptionHelper[F,a]. The F comes from the class declaration.
{! val oh: OptionFutureHelper[Int] = ! ! ! new OptionHelper[Future,Int](Future { Some(1) })! pipeline(oh) ! }! ! // We get BOTH parallelism and we short circuit on any None value. !
necessary methods. We still exploit the benefit of whatever F is passed in. We can nest multiple F’s to get multiple benefits just as we did with our OptionHelper with a Future.
get a plethora of instances of them for free too. Future, Option, List, Stream, Tuple, Either,\/ (right biased Either), Nel. ! ! OptionHelper is already written too, it is called OptionT (because it is the Option Monad Transformer). ! ! Mapable (Functor) and FlatMapable (Monad) are not the only ways to abstract over things!
! ! (implicit M: Monad[F]): EitherT[F, String, String] = {! val nm = EitherT(M.point(tryUnsafeApi(2)))! for {! i <- fa! s <- EitherT(M.point(tryUnsafeApi(i)))! s2 <- EitherT(M.point(tryUnsafeApi2(s + "blah")))! s3 <- nm! } yield ! s3 + s2! }! ! /*We can now short circuit getting an error message, and abstract over whatever F is (Future for parallelism for instance).*/!
you need ordering. If not, there is potential for concurrency. The more you abstract functions, often times the simpler it is to spot potential for concurrency.