Download - Monad Transformers In The Wild
![Page 1: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/1.jpg)
MONAD TRANSFORMERS
In The !ld
![Page 2: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/2.jpg)
speakerdeck.com/u/jrwest/p/monad-transformers
![Page 3: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/3.jpg)
TWITTER: @_JRWEST
GITHUB.COM/JRWEST
BLOG.LOOPEDSTRANGE.COM
![Page 4: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/4.jpg)
SF SCALA
May 2012
* http://marakana.com/s/scala_typeclassopedia_with_john_kodumal_of_atlassian_video,1198/index.html
![Page 5: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/5.jpg)
trait Monad[F[_]] extends Applicative[F] {
def flatMap[A, B](fa: F[A])(f :A=>F[B]):F[B]
}
* monad type class * flatMap also called bind, >>=
![Page 6: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/6.jpg)
def point[A](a: => A): M[A]
def map[A,B](ma: M[A])(f: A => B): M[B]
def flatMap[A,B](ma: M[A])(f: A => M[B]): M[B]
* the functions we care about* lift pure value, lift pure function, chain “operations”
![Page 7: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/7.jpg)
scala> import scalaz.Monad
scala> import scalaz.std.option._
scala> val a = Monad[Option].point(1)
a: Option[Int] = Some(1)scala> Monad[Option].map(a)(_.toString + "hi")
res2: Option[java.lang.String] = Some(1hi)
scala> Monad[Option].bind(a)(i => if (i < 0) None else Some(i + 1))res4: Option[Int] = Some(2)
* explicit type class usage in scalaz seven
![Page 8: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/8.jpg)
scala> import scalaz.syntax.monad._import scalaz.syntax.monad._scala> Option(1).flatMap(i => if (i < 0) None else Some(i+1))res6: Option[Int] = Some(2)scala> 1.point[Option].flatMap(...)res7: Option[Int] = Some(2)
* implicit type class usage in scalaz7 using syntax extensions
![Page 9: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/9.jpg)
“A MONADIC FOR
COMPREHENSION IS AN
EMBEDDED PROGRAMMING
LANGUAGE WITH SEMANTICS
DEFINED BY THE MONAD”
* “one intuition of monads” - john
![Page 10: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/10.jpg)
MULTIPLE EFFECTS
C"position
![Page 11: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/11.jpg)
Option[A]
* it may not exist
![Page 12: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/12.jpg)
SEMANTICSSIDE NOTE:
* to an extent, you can “choose” the meaning of a monad* Option -- anon. exceptions -- more narrowly, the exception that something is not there. Validation - monad/not monad - can mean different things in different contexts
![Page 13: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/13.jpg)
IO[Option[A]]
* but side-effects are needed to even look for that value
![Page 14: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/14.jpg)
IO[Validation[Throwable,Option[A]]
* and looking for that value may throw exceptions (or fail in some way)
![Page 15: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/15.jpg)
IO[(List[String], Validation[Throwable,Option[A])]
* and logging what is going on is necessary
![Page 16: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/16.jpg)
MULTIPLE EFFECTS
A P#oblem
![Page 17: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/17.jpg)
MONADS DO NOT
COMPOSE
* the problem in theory (core issue)
![Page 18: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/18.jpg)
“COMPOSE”?
![Page 19: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/19.jpg)
FUNCTORSDO
COMPOSE
* as well as applicatives
![Page 20: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/20.jpg)
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f :A=>B):F[B]
}
![Page 21: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/21.jpg)
def composeFunctor[M[_],N[_]](implicit m: Functor[M], n: Functor[N]) = new Functor[({type MN[A]=[M[N[A]]]})#MN] { def map[A,B](mna: M[N[A]])(f: A => B): M[N[B]] = ... }
* generic function that composes any two functors M[_] and N[_]
![Page 22: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/22.jpg)
def composeFunctor[M[_],N[_]](implicit m: Functor[M], n: Functor[N]) = new Functor[({type MN[A]=[M[N[A]]]})#MN] { def map[A,B](mna: M[N[A]])(f: A => B): M[N[B]] = { M.map(mna)(na => N.map(na)(f)) } }
![Page 23: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/23.jpg)
scala> Option("abc").map(f)res1: Option[Int] = Some(3)
scala> List(Option("abc"), Option("d"), Option("ef")).map2(f)res2: List[Option[Int]] = List(Some(3), Some(1), Some(2))
* can compose functors infinitely deep but...* scalaz provides method to compose 2, with nice syntatic sugar, easily (map2)
![Page 24: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/24.jpg)
def notPossible[M[_],N[_]](implicit m: Monad[M], n: Monad[N]) = new Monad[({type MN[A]=[M[N[A]]]})#MN] { def flatMap[A,B](mna: M[N[A]])(f: A => M[N[B]]): M[N[B]] = ... }
* cannot write the same function for any two monads M[_], N[_]
![Page 25: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/25.jpg)
def notPossible[M[_],N[_]](implicit m: Monad[M], n: Monad[N]) = new Monad[({type MN[A]=[M[N[A]]]})#MN] { def flatMap[A,B](mna: M[N[A]])(f: A => M[N[B]]): M[N[B]] = ... } TRY IT
!
* best way to understand this is attempt to write it yourself* it won’t compile
![Page 26: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/26.jpg)
http://blog.tmorris.net/monads-do-not-compose/
* good resource to dive into this in more detail* some of previous slides based on above* provides template, in the form of a gist, for trying this stuff out
![Page 27: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/27.jpg)
STAIR
STEPPING
* the problem in practice*http://www.flickr.com/photos/caliperstudio/2667302181/
![Page 28: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/28.jpg)
val a: IO[Option[MyData]] = ...
val b: IO[Option[MyData]] = ...
* have two values that require we communicate w/ outside world to fetch* those values may not exist (alternative meaning, fetching may result in exceptions that are anonymous)
![Page 29: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/29.jpg)
for { data1 <- a data2 <- b} yield { data1 merge data2 // fail}
* want to merge the two pieces of data if they both exist
![Page 30: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/30.jpg)
for { // we've escaped IO, fail d1 <- a.unsafePerformIO d2 <- b.unsafePerformIO} yield d1 merge d2
* don’t want to perform the actions until later (don’t escape the IO monad)
![Page 31: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/31.jpg)
for { od1 <- a od2 <- b} yield (od1,od2) match { case (Some(d1),Some(d2) => Option(d1 merge d2) case (a@Some(d1),_)) => a case (_,a@Some(d2)) => a case _ => None}
for { od1 <- a od2 <- b} yield for { d1 <- od1 d2 <- od2} yield d1 merge d2
* may notice the semi-group here* can also write it w/ an applicative* this is a contrived example
![Page 32: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/32.jpg)
def b(data: MyData): IO[Option[MyData]BUT WHAT IF...
* even w/ simple example, this minor change throws a monkey wrench in things
![Page 33: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/33.jpg)
for {
readRes <- readIO(domain)
res <- readRes.fold(
success = _.cata(
some = meta =>
if (meta.enabledStatus /== status) {
writeIO(meta.copy(enabledStatus = status))
} else meta.successNel[BarneyException].pure[IO],
none = new ReadFailure(domain).failNel[AppMetadata].pure[IO]
), failure = errors => errors.fail[AppMetadata].pure[IO]
) } yield res
):* example of what not to do from something I wrote a while back
![Page 34: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/34.jpg)
MULTIPLE EFFECTS
A Solution
![Page 35: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/35.jpg)
case class IOOption[A](run: IO[Option[A]])
define type that boxes box the value, doesn’t need to be a case class, similar to haskell newtype.
![Page 36: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/36.jpg)
new Monad[IOOption] {
def point[A](a: => A): IOOption[A] = IOOption(a.point[Option].point[IO])
def map[A,B](fa: IOOption[A])(f: A => B): IOOption[B] = IOOption(fa.run.map(opt => opt.map(f)))
def flatMap[A, B](fa: IOOption[A])(f :A=>IOOption[B]):IOOption[B] =
IOOption(fa.run.flatMap((o: Option[A]) => o match { case Some(a) => f(a).run case None => (None : Option[B]).point[IO] }))}
* can define a Monad instance for new type
![Page 37: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/37.jpg)
val a: IOOption[MyData] = ...val b: IOOption[MyData] = ...
val c: IOOption[MyData] = for { data1 <- a data2 <- b} yield { data1 merge data2}
val d: IO[Option[MyData]] = c.run
can use new type to improve previous contrived example
![Page 38: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/38.jpg)
type MyState[A] = State[StateData,A]case class MyStateOption[A](run: MyState[Option[A]])
* what if we don’t need effects, but state we can read and write to produce a final optional value and some new state* State[S,A] where S is fixed is a monad* can define a new type for that as well
![Page 39: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/39.jpg)
new Monad[MyStateOption] {
def map[A,B](fa: MyStateOption[A])(f: A => B): MyStateOption[B] =
MyStateOption(Functor[MyState].map(fa)(opt => opt.map(f)))
def flatMap[A, B](fa: MyStateOption[A])(f :A=>IOOption[B]) =
MyStateOption(Monad[MyState]].bind(fa)((o: Option[A]) => o match { case Some(a) => f(a).run case None => (None : Option[B]).point[MyState] }))}
new Monad[IOOption] {
def map[A,B](fa: IOOption[A])(f: A => B): IOOption[B] = IOOption(Functor[IO].map(fa)(opt => opt.map(f)))
def flatMap[A, B](fa: IOOption[A])(f :A=>IOOption[B]) =
IOOption(Monad[IO]].bind(fa)((o: Option[A]) => o match { case Some(a) => f(a).run case None => (None : Option[B]).point[IO] }))}
* opportunity for more abstraction* if you were going to do this, not exactly the way you would define these in real code, cheated a bit using {Functor,Monad}.apply
![Page 40: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/40.jpg)
case class OptionT[M[_], A](run: M[Option[A]])
define a new type parameterized * -> * and *.
![Page 41: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/41.jpg)
case class OptionT[M[_], A](run: M[Option[A]]) { def map[B](f: A => B)(implicit F: Functor[M]): OptionT[M,B] def flatMap[B](f: A => OptionT[M,B])(implicit M: Monad[M]): OptionT[M,B]}
* define map/flatMap a little differently, can be done like previous as typeclass instance but convention is to define the interface on the transformer and later define typeclass instance using the interface
![Page 42: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/42.jpg)
case class OptionT[M[_], A](run: M[Option[A]]) { def map[B](f: A => B)(implicit F: Functor[M]): OptionT[M,B] = OptionT[M,B](F.map(run)((o: Option[A]) => o map f)) def flatMap[B](f: A => OptionT[M,B])(implicit M: Monad[M]): OptionT[M,B] = OptionT[M,B](M.bind(run)((o: Option[A]) => o match { case Some(a) => f(a).run case None => M.point((None: Option[B])) }))}
* implementations resemble what has already been shown
![Page 43: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/43.jpg)
new Monad[IOOption] {
def map[A,B](fa: IOOption[A])(f: A => B): IOOption[B] = IOOption(Functor[IO].map(fa)(opt => opt.map(f)))
def flatMap[A, B](fa: IOOption[A])(f :A=>IOOption[B]) =
IOOption(Monad[IO]].bind(fa)((o: Option[A]) => o match { case Some(a) => f(a).run case None => (None : Option[B]).point[IO] }))}
case class OptionT[M[_], A](run: M[Option[A]]) { def map[B](f: A => B)(implicit F: Functor[M]): OptionT[M,B] = OptionT[M,B](F.map(run)((o: Option[A]) => o map f)) def flatMap[B](f: A => OptionT[M,B])(implicit M: Monad[M]) = OptionT[M,B](M.bind(run)((o: Option[A]) => o match { case Some(a) => f(a).run case None => M.point((None: Option[B])) }))}
* it the generalization of what was written before
![Page 44: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/44.jpg)
type FlowState[A] = State[ReqRespData, A]val f: Option[String] => FlowState[Boolean] = (etag: Option[String]) => { val a: OptionT[FlowState, Boolean] = for { // string <- OptionT[FlowState,String] e <- optionT[FlowState](etag.point[FlowState]) // wrap FlowState[Option[String]] in OptionT matches <- optionT[FlowState]((requestHeadersL member IfMatch)) } yield matches.split(",").map(_.trim).toList.contains(e) a getOrElse false // FlowState[Boolean]}
* check existence of etag in an http request, data lives in state* has minor bug, doesn’t deal w/ double quotes as written* https://github.com/stackmob/scalamachine/blob/master/core/src/main/scala/scalamachine/core/v3/WebmachineDecisions.scala#L282-285
![Page 45: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/45.jpg)
val reqCType: OptionT[FlowState,ContentType] = for { contentType <- optionT[FlowState]( (requestHeadersL member ContentTypeHeader) ) mediaInfo <- optionT[FlowState]( parseMediaTypes(contentType).headOption.point[FlowState] )} yield mediaInfo.mediaRange
* determine content type of the request, data lives in state, may not be specified* https://github.com/stackmob/scalamachine/blob/master/core/src/main/scala/scalamachine/core/v3/WebmachineDecisions.scala#L772-775
![Page 46: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/46.jpg)
scala> type EitherTString[M[_],A] = EitherT[M,String,A]defined type alias EitherTString
scala> val items = eitherT[List,String,Int](List(1,2,3,4,5,6).map(Right(_)))items: scalaz.EitherT[List,String,Int] = ...
* adding features to a “embedded language”
![Page 47: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/47.jpg)
for { i <- items } yield print(i)// 123456
for { i <- items _ <- if (i > 4) leftT[List,String,Unit]("fail") else rightT[List,String,Unit](()) } yield print(i)// 1234
* adding error handling, and early termination to non-deterministic computation
![Page 48: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/48.jpg)
MONAD
TRANSFORMERS
In General
![Page 49: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/49.jpg)
MyMonad[A]
![Page 50: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/50.jpg)
NAMING CONVENTION
MyMonadT[M[_], A]
* transformer name ends in T
![Page 51: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/51.jpg)
BOXES A VALUE
run: M[MyMonad[A]
* value is typically called “run” in scalaz7* often called “value” in scalaz6 (because of NewType)
![Page 52: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/52.jpg)
A MONAD TRANSFORMER
IS A MONAD TOO
* i mean, its thats kinda the point of this whole exercise isn’t it :)
![Page 53: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/53.jpg)
def optTMonad[M[_] : Monad] = new Monad[({type O[X]=OptionT[M,X]]})#O) { def point[A](a: => A): OptionT[M,A] = OptionT(a.point[Option].point[M]) def map[A,B](fa: OptionT[M,A])(f: A => B): OptionT[M,B] = fa map f def flatMap[A, B](fa: OptionT[M,A])(f :A=> OptionT[M,B]): OptionT[M, B] = fa flatMap f}
* monad instance definition for OptionT
![Page 54: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/54.jpg)
HAS INTERFACE RESEMBLING UNDERLYING
MONAD’S INTERFACE
* can interact with the monad transformer in a manner similar to working with the actual monad* same methods, slightly different type signatures* different from haskell, “feature” of scala, since we can define methods on a type
![Page 55: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/55.jpg)
case class OptionT[M[_], A](run: M[Option[A]]) { def getOrElse[AA >: A](d: => AA)(implicit F: Functor[M]): M[AA] = F.map(run)((_: Option[A]) getOrElse default) def orElse[AA >: A](o: OptionT[M,AA])(implicit M: Monad[M]): OptionT[M,AA] = OptionT[M,AA](M.bind(run) { case x@Some(_) => M.point(x) case None => o.run }}
![Page 56: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/56.jpg)
MONAD
TRANSFORMERS
Stacked Effects
![Page 57: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/57.jpg)
TRANSFORMER IS A MONAD
⇒TRANSFORMER CAN WRAP ANOTHER TRANSFORMER
* at the start, the goal was to stack effects (not just stack 2 effects)* this makes it possible
![Page 58: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/58.jpg)
type VIO[A] = ValidationT[IO,Throwable,A]
def doWork(): VIO[Option[Int]] = ...
val r: OptionT[VIO,Int] = optionT[VIO](doWork())
* wrap the ValidationT with success type Option[A] in an OptionT* define type alias for connivence -- avoids nasty type lambda syntax inline
![Page 59: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/59.jpg)
val action: OptionT[VIO, Boolean] = for { devDomain <- optionT[VIO] { validationT( bucket.fetch[CName]("%s.%s".format(devPrefix,hostname)) ).mapFailure(CNameServiceException(_)) } _ <- optionT[VIO] { validationT(deleteDomains(devDomain)).map(_.point[Option]) }} yield true
* code (slightly modified) from one of stackmob’s internal services* uses Scaliak to fetch hostname data from riak and then remove them* possible to clean this code up a bit, will discuss shortly (monadtrans)
![Page 60: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/60.jpg)
KEEP ON STACKIN’
ON
* don’t have to stop at 2 levels deep, our new stack is monad too* each monad/transformer we add to the stack compose more types of effects
![Page 61: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/61.jpg)
“ORDER” MATTERS
* how stack is built, which transformers wrap which monads, determines the overall semantics of the entire stack* changing that order can, and usually does, change semantics
![Page 62: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/62.jpg)
OptionT[FlowState, A]vs.
StateT[Option,ReqRespData,A]
* what is the difference in semantics between the two? * type FlowState[A] = State[ReqRespData,A]
![Page 63: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/63.jpg)
FlowState[Option[A]]vs.
Option[State[ReqRespData,A]
* unboxing makes things easier to see* a state action that returns an optional value vs a state action that may not exist* the latter probably doesn’t make as much sense in the majority of cases
![Page 64: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/64.jpg)
MONADTRANS
The Type Class
* type classes beget more type classes
![Page 65: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/65.jpg)
REMOVING REPETITION===
MORE ABSTRACTION
* previous examples have had a repetitive, annoying, & verbose task* can be abstracted away...by a type class of course
![Page 66: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/66.jpg)
optionT[VIO](validationT(deleteDomains(devDomain)).map(_.point[Option]))eitherT[List,String,Int](List(1,2,3,4,5,6).map(Right(_))) resT[FlowState](encodeBodyIfSet(resource).map(_.point[Res]))
* some cases require lifting the value into the monad and then wrap it in the transformer* from previous examples
![Page 67: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/67.jpg)
M[A] -> M[N[A]] -> NT[M[N[_]], A]
* this is basically what we are doing every time* taking some monad M[A], lifting A into N, a monad we have a transformer for, and then wrapping all of that in N’s monad transformer
![Page 68: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/68.jpg)
trait MonadTrans[F[_[_], _]] {
def liftM[G[_] : Monad, A](a: G[A]): F[G, A]
}
* liftM will do this for any transformer F[_[_],_] and any monad G[_] provided an instance of it is defined for F[_[_],_]
![Page 69: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/69.jpg)
def liftM[G[_], A](a: G[A])(implicit G: Monad[G]): OptionT[G, A] =
OptionT[G, A](G.map[A, Option[A]](a)((a: A) => a.point[Option]))
* full definition requires some type ceremony* https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/OptionT.scala#L155-156
![Page 70: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/70.jpg)
def liftM[G[_], A](ga: G[A])(implicit G: Monad[G]): ResT[G,A] =
ResT[G,A](G.map(ga)(_.point[Res]))
* implementation for scalamachine’s Res monad* https://github.com/stackmob/scalamachine/blob/master/scalaz7/src/main/scala/scalamachine/scalaz/res/ResT.scala#L75-76
![Page 71: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/71.jpg)
encodeBodyIfSet(resource).liftM[OptionT]List(1,2,3).liftM[EitherTString]validationT(deleteDomains(devDomain)).liftM[OptionT]
* cleanup of previous examples* method-like syntax requires a bit more work: https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/syntax/MonadSyntax.scala#L9
![Page 72: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/72.jpg)
for { media <- (metadataL >=> contentTypeL).map(_ | ContentType("text/plain")).liftM[ResT] charset <- (metadataL >=> chosenCharsetL).map2(";charset=" + _).getOrElse("")).liftM[ResT] _ <- (responseHeadersL += (ContentTypeHeader, media.toHeader + charset)).liftM[ResT] mbHeader <- (requestHeadersL member AcceptEncoding).liftM[ResT] decision <- mbHeader >| f7.point[ResTFlow] | chooseEncoding(resource, "identity;q=1.0,*;q=0.5")} yield decision
* https://github.com/stackmob/scalamachine/blob/master/core/src/main/scala/scalamachine/core/v3/WebmachineDecisions.scala#L199-205
![Page 73: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/73.jpg)
MONAD
TRANSFORMERS
In Review
![Page 74: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/74.jpg)
STACKINGMONADS
COMPOSES EFFECTS
* when monads are stacked an embedded language is being built with multiple effects* this is not the only intuition of monads/transformers
![Page 75: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/75.jpg)
CAN NOT COMPOSE MONADS
GENERICALLY
* cannot write generic function to compose any two monads M[_], N[_] like we can for any two functors
![Page 76: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/76.jpg)
MONAD TRANSFORMERSCOMPOSE M[_] : MONAD WITH
ANY N[_] : MONAD
* can’t compose any two, but can compose a given one with any other
![Page 77: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/77.jpg)
MONAD TRANSFORMERS WRAP OTHER
MONAD TRANSFORMERS
* monad transformers are monads* so they can be the N[_] : Monad that the transformer composes with its underlying monad
![Page 78: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/78.jpg)
MONADTRANSREDUCES
REPETITION
* often need to take a value that is not entirely lifted into a monad transformer stack and do just that
![Page 79: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/79.jpg)
STACK MONADSDON’T
STAIR-STEP
* monad transformers reduce ugly, stair-stepping or nested code and focuses on core task* focuses on intuition of mutiple effects instead of handling things haphazardly
![Page 80: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/80.jpg)
THANK YOU
* stackmob, markana, john & atlassian, other sponsors, cosmin
![Page 81: Monad Transformers In The Wild](https://reader030.vdocuments.mx/reader030/viewer/2022020110/554a5864b4c905572f8b4db3/html5/thumbnails/81.jpg)
QUESTIONS?