THIS TALK: WHAT AND WHY What: The talk I wished I attended before banging my head against this Why: Because I still remember how it was before knowing it @gabro27 Scala Days 2017 - Chicago
THE PROBLEM val x: Future[List[Int]] = ??? futureList.map(list => list.map(f)) ^ ^ |________________| 2 maps 1 function @gabro27 Scala Days 2017 - Chicago
FUNCTOR OF FUTURE val futureF = new Functor[Future] { def map[A, B](fa: Future[A])(f: A => B): Future[B] = fa.map(f) } @gabro27 Scala Days 2017 - Chicago
IN PRACTICE import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global import cats._; import std.future._; import std.list._ // create a `Functor[Future[List]` val futureListF = Functor[Future].compose(Functor[List]) val data: Future[List[Int]] = Future(List(1, 2, 3)) // only one map! futureListF.map(data)(_ + 1) // Future(List(2, 3, 4)) @gabro27 Scala Days 2017 - Chicago
IN OTHER WORDS when life gives you F[F[A]] you probably wanted flatMap e.g. val f: Future[Future[Int]] = Future(42).map(x => Future(24)) val g: Future[Int] = Future(42).flatMap(x => Future(24)) @gabro27 Scala Days 2017 - Chicago
A LESS CONTRIVED EXAMPLE def getUser(name: String): Future[User] def getAddress(user: User): Future[Address] val getCity: Future[String] = getUser("Gabriele").flatMap( gab => getAddress(gab).map( address => address.city ) ) @gabro27 Scala Days 2017 - Chicago
BACK TO THE REAL WORLD def getUser(name: String): Future[Option[User]] // better def getAddress(user: User): Future[Option[Address]] @gabro27 Scala Days 2017 - Chicago
DO YOU EVEN YIELD, BRO? val city: Future[Option[String]] = for { maybeGab } yield for { gab } yield for { maybeAddress } yield for { address } yield address.city @gabro27 Scala Days 2017 - Chicago
AND USE val f: FutOpt[String] = for { gab address } yield address.city // ! val city: Future[Option[String]] = f.value @gabro27 Scala Days 2017 - Chicago
MEET OptionT val f: OptionT[Future, String] = for { gab address } yield address.city // ! val city: Future[Option[String]] = f.value @gabro27 Scala Days 2017 - Chicago
HOW ABOUT case class MyError(msg: String) type ResultT[F[_], A] = EitherT[F, MyError, A]] type FutureResult[A] = ResultT[Future, A] @gabro27 Scala Days 2017 - Chicago
TIP #2 keep your transformers for youself def publicApiMethod(x: String): OptionT[Future, Int] = ! def publicApiMethod(x: String): Future[Option[Int]] = " by the way val x: OptionT[Future, Int] = OptionT(Future(Option(42))) val y: Future[Option[Int]] = x.value // Future(Option(42)) @gabro27 Scala Days 2017 - Chicago
TIP #3 ! Perf! Wrapping/unwrapping isn't cheap, so if you're concerned about performance, consider benchmarking your code. @gabro27 Scala Days 2017 - Chicago
MONAD TRANSFORMERS: TAKEAWAYS > they end with T > F[G[X]] becomes GT[F[_], X] > can be stacked undefinitely, but gets awkward > they are a tool for working with stacked monads @gabro27 Scala Days 2017 - Chicago
FREE MONADS > clearly separate structure and interpretation > effects are separated from program definition http://typelevel.org/cats/datatypes/freemonad.html @gabro27 Scala Days 2017 - Chicago
EFF https://github.com/atnos-org/eff-cats "Extensible effects are an alternative to monad transformers for computing with effects in a functional way" based on Freer Monads, More Extensible Effects by Oleg Kiselyov @gabro27 Scala Days 2017 - Chicago