SlideShare ist ein Scribd-Unternehmen logo
1 von 25
Downloaden Sie, um offline zu lesen
Reducing Boilerplate and Combining
Effects:
A Monad Transformer Example
Scala Matsuri - Feb 25th, 2017
Connie Chen
:
Hello
• @coni
• Data Platform team @ Twilio
• @ http://github.com/conniec
• Monad transformers allow different monads to
compose
• Combine effects of monads to create a
SUPER MONAD
• Eg. Future[Option], Future[Either],
Reader[Option]
• In this example, we will use the Cats library...
What are Monad transformers?
Future[Either[A, B]] turns into
EitherT[Future, A, B]
Future[Option[A]] turns into
OptionT[Future, A]
import scala.concurrent.Future
import cats.data.OptionT
import cats.implicits._
import
scala.concurrent.ExecutionContext.Implicits.glo
bal
case class Beans(fresh: Boolean = true)
case class Grounds()
class GroundBeansException(s: String) extends
Exception(s: String)
1.
Example: Making coffee!
Step 1. Grind the beans
def grindFreshBeans(beans: Beans, clumsy: Boolean =
false): Future[Option[Grounds]] = {
if (clumsy) {
Future.failed(new GroundBeansException("We are bad
at grinding"))
} else if (beans.fresh) {
Future.successful(Option(Grounds()))
} else {
Future.successful(None)
}
}
1.
Example: Making coffee!
Step 1. Grind the beans
Step 1. Grind the beans
Three different kind of results:
• Value found
• Value not found
• Future failed
Future 3
Example: Making coffee!
Step 2. Boil hot water
case class Kettle(filled: Boolean = true)
case class Water()
case class Coffee(delicious: Boolean)
class HotWaterException(s: String) extends
Exception(s: String)
2.
def getHotWater(kettle: Kettle, clumsy: Boolean =
false): Future[Option[Water]] = {
if (clumsy) {
Future.failed(new HotWaterException("Ouch
spilled that water!"))
} else if (kettle.filled) {
Future.successful(Option(Water()))
} else {
Future.successful(None)
}
}
Step 3. Combine water and coffee (it's a pourover)
3. ( )
def makingCoffee(grounds: Grounds, water: Water):
Future[Coffee] = {
println(s"Making coffee with... $grounds and
$water")
Future.successful(Coffee(delicious=true))
}
val coffeeFut = for {
} yield Option(result)
coffeeFut.onSuccess {
case Some(s) => println(s"SUCCESS: $s")
case None => println("No coffee found?")
}
coffeeFut.onFailure {
case x => println(s"FAIL: $x")
}
Without Monad transformers, success scenario
beans <- grindFreshBeans(Beans(fresh=true))
hotWater <- getHotWater(Kettle(filled=true))
beansResult = beans.getOrElse(throw new Exception("Beans result
errored. "))
waterResult = hotWater.getOrElse(throw new Exception("Water
result errored. "))
result <- makingCoffee(beansResult, waterResult)
Without Monad transformers, success scenario
coffeeFut:
scala.concurrent.Future[Option[Coffee]] =
scala.concurrent.impl.Promise
$DefaultPromise@7404ac2
scala> Making coffee with... Grounds() and
Water()
SUCCESS: Coffee(true)
With Monad transformers, success scenario
val coffeeFutMonadT = for {
beans <- OptionT(grindFreshBeans(Beans(fresh=true)))
hotWater <- OptionT(getHotWater(Kettle(filled=true)))
result <- OptionT.liftF(makingCoffee(beans, hotWater))
} yield result
coffeeFutMonadT.value.onSuccess {
case Some(s) => println(s"SUCCESS: $s")
case None => println("No coffee found?")
}
coffeeFutMonadT.value.onFailure {
case x => println(s"FAIL: $x")
}
coffeeFutMonadT:
cats.data.OptionT[scala.concurrent.Future,
Coffee] =
OptionT(scala.concurrent.impl.Promise
$DefaultPromise@4a1c4b40)
scala> Making coffee with... Grounds() and
Water()
SUCCESS: Coffee(true)
With Monad transformers, success scenario
OptionT
`fromOption` gives you an OptionT from Option
Internally, it is wrapping your option in a Future.successful()
`liftF` gives you an OptionT from Future
Internally, it is mapping on your Future and wrapping it in a
Some()
Helper functions on OptionT
val coffeeFut = for {
beans <- grindFreshBeans(Beans(fresh=false))
hotWater <- getHotWater(Kettle(filled=true))
beansResult = beans.getOrElse(throw new Exception("Beans result
errored. "))
waterResult = hotWater.getOrElse(throw new Exception("Water result
errored. "))
result <- makingCoffee(beansResult, waterResult)
} yield Option(result)
coffeeFut.onSuccess {
case Some(s) => println(s"SUCCESS: $s")
case None => println("No coffee found?")
}
coffeeFut.onFailure {
case x => println(s"FAIL: $x")
}
Without Monad transformers, failure scenario
Without Monad transformers, failure scenario
coffeeFut:
scala.concurrent.Future[Option[Coffee]] =
scala.concurrent.impl.Promise
$DefaultPromise@17ee3bd8
scala> FAIL: java.lang.Exception: Beans
result errored.
val coffeeFutT = for {
beans <- OptionT(grindFreshBeans(Beans(fresh=false)))
hotWater <- OptionT(getHotWater(Kettle(filled=true)))
result <- OptionT.liftF(makingCoffee(beans,
hotWater))
} yield result
coffeeFutT.value.onSuccess {
case Some(s) => println(s"SUCCESS: $s")
case None => println("No coffee found?")
}
coffeeFutT.value.onFailure {
case x => println(s"FAIL: $x")
}
With Monad transformers, failure scenario
With Monad transformers, failure scenario
coffeeFutT:
cats.data.OptionT[scala.concurrent.Future
,Coffee] =
OptionT(scala.concurrent.impl.Promise
$DefaultPromise@4e115bbc)
scala> No coffee found?
val coffeeFutT = for {
beans <- OptionT(grindFreshBeans(Beans(fresh=true)))
hotWater <- OptionT(getHotWater(Kettle(filled=true),
clumsy=true))
result <- OptionT.liftF(makingCoffee(beans,
hotWater))
} yield s"$result"
coffeeFutT.value.onSuccess {
case Some(s) => println(s"SUCCESS: $s")
case None => println("No coffee found?")
}
coffeeFutT.value.onFailure {
case x => println(s"FAIL: $x")
}
With monad transformers, failure scenario with
exception
FAIL: $line86.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw
$HotWaterException: Ouch spilled that water!
coffeeFutT:
cats.data.OptionT[scala.concurrent.Future,Coffee] =
OptionT(scala.concurrent.impl.Promise
$DefaultPromise@20e4013)
With monad transformers, failure scenario with
exception
flatMap
• Use monad transformers to short circuit your monads
What did we learn?
• Instead of unwrapping layers of monads, monad
transformers results in a new monad to flatMap with
• Reduce layers of x.map( y => y.map ( ... )) to just
x.map ( y => ...))
x.map ( y => y.map ( ... ) ) map
OptionT
What’s next?
• Many other types of monad transformers: ReaderT,
WriterT, EitherT, StateT
• Since monad transformers give you a monad as a
result-- you can stack them too!
Thank you
Connie Chen - @coni
Twilio
We’re hiring!
final case class OptionT[F[_], A](value: F[Option[A]]) {
def fold[B](default: => B)(f: A => B)(implicit F:
Functor[F]): F[B] =
F.map(value)(_.fold(default)(f))
def map[B](f: A => B)(implicit F: Functor[F]):
OptionT[F, B] =
OptionT(F.map(value)(_.map(f)))
def flatMapF[B](f: A => F[Option[B]])(implicit F:
Monad[F]): OptionT[F, B] =
OptionT(F.flatMap(value)(_.fold(F.pure[Option[B]]
(None))(f)))
OptionT implementation
def liftF[F[_], A](fa: F[A])(implicit F:
Functor[F]): OptionT[F, A] = OptionT(F.map(fa)
(Some(_)))
OptionT implementation

Weitere ähnliche Inhalte

Andere mochten auch

Scala Warrior and type-safe front-end development with Scala.js
Scala Warrior and type-safe front-end development with Scala.jsScala Warrior and type-safe front-end development with Scala.js
Scala Warrior and type-safe front-end development with Scala.js
takezoe
 
React Nativeはクロスプラットフォームモバイルアプリ開発の夢を見るか #DroidKaigi
React Nativeはクロスプラットフォームモバイルアプリ開発の夢を見るか #DroidKaigiReact Nativeはクロスプラットフォームモバイルアプリ開発の夢を見るか #DroidKaigi
React Nativeはクロスプラットフォームモバイルアプリ開発の夢を見るか #DroidKaigi
Yukiya Nakagawa
 
DevOpsとか言う前にAWSエンジニアに知ってほしいアプリケーションのこと
DevOpsとか言う前にAWSエンジニアに知ってほしいアプリケーションのことDevOpsとか言う前にAWSエンジニアに知ってほしいアプリケーションのこと
DevOpsとか言う前にAWSエンジニアに知ってほしいアプリケーションのこと
Terui Masashi
 

Andere mochten auch (20)

Scala Warrior and type-safe front-end development with Scala.js
Scala Warrior and type-safe front-end development with Scala.jsScala Warrior and type-safe front-end development with Scala.js
Scala Warrior and type-safe front-end development with Scala.js
 
「ハイレベルメンバーを共創させたら何が起きるか?実験」結果報告会 スライド
「ハイレベルメンバーを共創させたら何が起きるか?実験」結果報告会 スライド「ハイレベルメンバーを共創させたら何が起きるか?実験」結果報告会 スライド
「ハイレベルメンバーを共創させたら何が起きるか?実験」結果報告会 スライド
 
Akka-chan's Survival Guide for the Streaming World
Akka-chan's Survival Guide for the Streaming WorldAkka-chan's Survival Guide for the Streaming World
Akka-chan's Survival Guide for the Streaming World
 
Deadly Code! (seriously) Blocking &amp; Hyper Context Switching Pattern
Deadly Code! (seriously) Blocking &amp; Hyper Context Switching PatternDeadly Code! (seriously) Blocking &amp; Hyper Context Switching Pattern
Deadly Code! (seriously) Blocking &amp; Hyper Context Switching Pattern
 
Van laarhoven lens
Van laarhoven lensVan laarhoven lens
Van laarhoven lens
 
アクセシビリティはじめました
アクセシビリティはじめましたアクセシビリティはじめました
アクセシビリティはじめました
 
BCU30 - Webでできるマルチメディア表現
BCU30 - Webでできるマルチメディア表現BCU30 - Webでできるマルチメディア表現
BCU30 - Webでできるマルチメディア表現
 
Scala Matsuri 2017
Scala Matsuri 2017Scala Matsuri 2017
Scala Matsuri 2017
 
Pratical eff
Pratical effPratical eff
Pratical eff
 
Going bananas with recursion schemes for fixed point data types
Going bananas with recursion schemes for fixed point data typesGoing bananas with recursion schemes for fixed point data types
Going bananas with recursion schemes for fixed point data types
 
React Nativeはクロスプラットフォームモバイルアプリ開発の夢を見るか #DroidKaigi
React Nativeはクロスプラットフォームモバイルアプリ開発の夢を見るか #DroidKaigiReact Nativeはクロスプラットフォームモバイルアプリ開発の夢を見るか #DroidKaigi
React Nativeはクロスプラットフォームモバイルアプリ開発の夢を見るか #DroidKaigi
 
DevOpsとか言う前にAWSエンジニアに知ってほしいアプリケーションのこと
DevOpsとか言う前にAWSエンジニアに知ってほしいアプリケーションのことDevOpsとか言う前にAWSエンジニアに知ってほしいアプリケーションのこと
DevOpsとか言う前にAWSエンジニアに知ってほしいアプリケーションのこと
 
Akka Cluster and Auto-scaling
Akka Cluster and Auto-scalingAkka Cluster and Auto-scaling
Akka Cluster and Auto-scaling
 
今から始める Lens/Prism
今から始める Lens/Prism今から始める Lens/Prism
今から始める Lens/Prism
 
AWSでアプリ開発するなら 知っておくべこと
AWSでアプリ開発するなら 知っておくべことAWSでアプリ開発するなら 知っておくべこと
AWSでアプリ開発するなら 知っておくべこと
 
Big Data Analytics Tokyo
Big Data Analytics TokyoBig Data Analytics Tokyo
Big Data Analytics Tokyo
 
The state of sbt 0.13, sbt server, and sbt 1.0 (ScalaMatsuri ver)
The state of sbt 0.13, sbt server, and sbt 1.0 (ScalaMatsuri ver)The state of sbt 0.13, sbt server, and sbt 1.0 (ScalaMatsuri ver)
The state of sbt 0.13, sbt server, and sbt 1.0 (ScalaMatsuri ver)
 
バッチを Akka Streams で再実装したら100倍速くなった話 #ScalaMatsuri
バッチを Akka Streams で再実装したら100倍速くなった話 #ScalaMatsuriバッチを Akka Streams で再実装したら100倍速くなった話 #ScalaMatsuri
バッチを Akka Streams で再実装したら100倍速くなった話 #ScalaMatsuri
 
あなたのScalaを爆速にする7つの方法(日本語版)
あなたのScalaを爆速にする7つの方法(日本語版)あなたのScalaを爆速にする7つの方法(日本語版)
あなたのScalaを爆速にする7つの方法(日本語版)
 
Reactive integrations with Akka Streams
Reactive integrations with Akka StreamsReactive integrations with Akka Streams
Reactive integrations with Akka Streams
 

Ähnlich wie Reducing Boilerplate and Combining Effects: A Monad Transformer Example

For this homework, you will develop a class called VendingMachine th.pdf
For this homework, you will develop a class called VendingMachine th.pdfFor this homework, you will develop a class called VendingMachine th.pdf
For this homework, you will develop a class called VendingMachine th.pdf
info309708
 

Ähnlich wie Reducing Boilerplate and Combining Effects: A Monad Transformer Example (11)

Coffeescript slides
Coffeescript slidesCoffeescript slides
Coffeescript slides
 
test
testtest
test
 
Joe Bew - Apprendi un nuovo linguaggio sfruttando il TDD e il Clean Code - Co...
Joe Bew - Apprendi un nuovo linguaggio sfruttando il TDD e il Clean Code - Co...Joe Bew - Apprendi un nuovo linguaggio sfruttando il TDD e il Clean Code - Co...
Joe Bew - Apprendi un nuovo linguaggio sfruttando il TDD e il Clean Code - Co...
 
Camel vs Spring EIP JAVA DSL
Camel vs Spring EIP JAVA DSLCamel vs Spring EIP JAVA DSL
Camel vs Spring EIP JAVA DSL
 
For this homework, you will develop a class called VendingMachine th.pdf
For this homework, you will develop a class called VendingMachine th.pdfFor this homework, you will develop a class called VendingMachine th.pdf
For this homework, you will develop a class called VendingMachine th.pdf
 
Damn Fine CoffeeScript
Damn Fine CoffeeScriptDamn Fine CoffeeScript
Damn Fine CoffeeScript
 
Getting property based testing to work after struggling for 3 years
Getting property based testing to work after struggling for 3 yearsGetting property based testing to work after struggling for 3 years
Getting property based testing to work after struggling for 3 years
 
Coffeescript - take a sip of code
Coffeescript - take a sip of codeCoffeescript - take a sip of code
Coffeescript - take a sip of code
 
Php tour 2018 un autre regard sur la validation (1)
Php tour 2018   un autre regard sur la validation (1)Php tour 2018   un autre regard sur la validation (1)
Php tour 2018 un autre regard sur la validation (1)
 
Chaining and function composition with lodash / underscore
Chaining and function composition with lodash / underscoreChaining and function composition with lodash / underscore
Chaining and function composition with lodash / underscore
 
Quick coffeescript
Quick coffeescriptQuick coffeescript
Quick coffeescript
 

Kürzlich hochgeladen

%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
masabamasaba
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
masabamasaba
 
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 

Kürzlich hochgeladen (20)

MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
 
%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto%in Soweto+277-882-255-28 abortion pills for sale in soweto
%in Soweto+277-882-255-28 abortion pills for sale in soweto
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
WSO2Con2024 - From Code To Cloud: Fast Track Your Cloud Native Journey with C...
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Toronto Psychic Readings, Attraction spells,Brin...
 
What Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the SituationWhat Goes Wrong with Language Definitions and How to Improve the Situation
What Goes Wrong with Language Definitions and How to Improve the Situation
 
tonesoftg
tonesoftgtonesoftg
tonesoftg
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
 
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Knoxville Psychic Readings, Attraction spells,Br...
 
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
Abortion Pills In Pretoria ](+27832195400*)[ 🏥 Women's Abortion Clinic In Pre...
 
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni%in Benoni+277-882-255-28 abortion pills for sale in Benoni
%in Benoni+277-882-255-28 abortion pills for sale in Benoni
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 

Reducing Boilerplate and Combining Effects: A Monad Transformer Example

  • 1. Reducing Boilerplate and Combining Effects: A Monad Transformer Example Scala Matsuri - Feb 25th, 2017 Connie Chen :
  • 2. Hello • @coni • Data Platform team @ Twilio • @ http://github.com/conniec
  • 3. • Monad transformers allow different monads to compose • Combine effects of monads to create a SUPER MONAD • Eg. Future[Option], Future[Either], Reader[Option] • In this example, we will use the Cats library... What are Monad transformers?
  • 4. Future[Either[A, B]] turns into EitherT[Future, A, B] Future[Option[A]] turns into OptionT[Future, A]
  • 5. import scala.concurrent.Future import cats.data.OptionT import cats.implicits._ import scala.concurrent.ExecutionContext.Implicits.glo bal case class Beans(fresh: Boolean = true) case class Grounds() class GroundBeansException(s: String) extends Exception(s: String) 1. Example: Making coffee! Step 1. Grind the beans
  • 6. def grindFreshBeans(beans: Beans, clumsy: Boolean = false): Future[Option[Grounds]] = { if (clumsy) { Future.failed(new GroundBeansException("We are bad at grinding")) } else if (beans.fresh) { Future.successful(Option(Grounds())) } else { Future.successful(None) } } 1. Example: Making coffee! Step 1. Grind the beans
  • 7. Step 1. Grind the beans Three different kind of results: • Value found • Value not found • Future failed Future 3 Example: Making coffee!
  • 8. Step 2. Boil hot water case class Kettle(filled: Boolean = true) case class Water() case class Coffee(delicious: Boolean) class HotWaterException(s: String) extends Exception(s: String) 2. def getHotWater(kettle: Kettle, clumsy: Boolean = false): Future[Option[Water]] = { if (clumsy) { Future.failed(new HotWaterException("Ouch spilled that water!")) } else if (kettle.filled) { Future.successful(Option(Water())) } else { Future.successful(None) } }
  • 9. Step 3. Combine water and coffee (it's a pourover) 3. ( ) def makingCoffee(grounds: Grounds, water: Water): Future[Coffee] = { println(s"Making coffee with... $grounds and $water") Future.successful(Coffee(delicious=true)) }
  • 10. val coffeeFut = for { } yield Option(result) coffeeFut.onSuccess { case Some(s) => println(s"SUCCESS: $s") case None => println("No coffee found?") } coffeeFut.onFailure { case x => println(s"FAIL: $x") } Without Monad transformers, success scenario beans <- grindFreshBeans(Beans(fresh=true)) hotWater <- getHotWater(Kettle(filled=true)) beansResult = beans.getOrElse(throw new Exception("Beans result errored. ")) waterResult = hotWater.getOrElse(throw new Exception("Water result errored. ")) result <- makingCoffee(beansResult, waterResult)
  • 11. Without Monad transformers, success scenario coffeeFut: scala.concurrent.Future[Option[Coffee]] = scala.concurrent.impl.Promise $DefaultPromise@7404ac2 scala> Making coffee with... Grounds() and Water() SUCCESS: Coffee(true)
  • 12. With Monad transformers, success scenario val coffeeFutMonadT = for { beans <- OptionT(grindFreshBeans(Beans(fresh=true))) hotWater <- OptionT(getHotWater(Kettle(filled=true))) result <- OptionT.liftF(makingCoffee(beans, hotWater)) } yield result coffeeFutMonadT.value.onSuccess { case Some(s) => println(s"SUCCESS: $s") case None => println("No coffee found?") } coffeeFutMonadT.value.onFailure { case x => println(s"FAIL: $x") }
  • 13. coffeeFutMonadT: cats.data.OptionT[scala.concurrent.Future, Coffee] = OptionT(scala.concurrent.impl.Promise $DefaultPromise@4a1c4b40) scala> Making coffee with... Grounds() and Water() SUCCESS: Coffee(true) With Monad transformers, success scenario
  • 14. OptionT `fromOption` gives you an OptionT from Option Internally, it is wrapping your option in a Future.successful() `liftF` gives you an OptionT from Future Internally, it is mapping on your Future and wrapping it in a Some() Helper functions on OptionT
  • 15. val coffeeFut = for { beans <- grindFreshBeans(Beans(fresh=false)) hotWater <- getHotWater(Kettle(filled=true)) beansResult = beans.getOrElse(throw new Exception("Beans result errored. ")) waterResult = hotWater.getOrElse(throw new Exception("Water result errored. ")) result <- makingCoffee(beansResult, waterResult) } yield Option(result) coffeeFut.onSuccess { case Some(s) => println(s"SUCCESS: $s") case None => println("No coffee found?") } coffeeFut.onFailure { case x => println(s"FAIL: $x") } Without Monad transformers, failure scenario
  • 16. Without Monad transformers, failure scenario coffeeFut: scala.concurrent.Future[Option[Coffee]] = scala.concurrent.impl.Promise $DefaultPromise@17ee3bd8 scala> FAIL: java.lang.Exception: Beans result errored.
  • 17. val coffeeFutT = for { beans <- OptionT(grindFreshBeans(Beans(fresh=false))) hotWater <- OptionT(getHotWater(Kettle(filled=true))) result <- OptionT.liftF(makingCoffee(beans, hotWater)) } yield result coffeeFutT.value.onSuccess { case Some(s) => println(s"SUCCESS: $s") case None => println("No coffee found?") } coffeeFutT.value.onFailure { case x => println(s"FAIL: $x") } With Monad transformers, failure scenario
  • 18. With Monad transformers, failure scenario coffeeFutT: cats.data.OptionT[scala.concurrent.Future ,Coffee] = OptionT(scala.concurrent.impl.Promise $DefaultPromise@4e115bbc) scala> No coffee found?
  • 19. val coffeeFutT = for { beans <- OptionT(grindFreshBeans(Beans(fresh=true))) hotWater <- OptionT(getHotWater(Kettle(filled=true), clumsy=true)) result <- OptionT.liftF(makingCoffee(beans, hotWater)) } yield s"$result" coffeeFutT.value.onSuccess { case Some(s) => println(s"SUCCESS: $s") case None => println("No coffee found?") } coffeeFutT.value.onFailure { case x => println(s"FAIL: $x") } With monad transformers, failure scenario with exception
  • 20. FAIL: $line86.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw $HotWaterException: Ouch spilled that water! coffeeFutT: cats.data.OptionT[scala.concurrent.Future,Coffee] = OptionT(scala.concurrent.impl.Promise $DefaultPromise@20e4013) With monad transformers, failure scenario with exception
  • 21. flatMap • Use monad transformers to short circuit your monads What did we learn? • Instead of unwrapping layers of monads, monad transformers results in a new monad to flatMap with • Reduce layers of x.map( y => y.map ( ... )) to just x.map ( y => ...)) x.map ( y => y.map ( ... ) ) map
  • 22. OptionT What’s next? • Many other types of monad transformers: ReaderT, WriterT, EitherT, StateT • Since monad transformers give you a monad as a result-- you can stack them too!
  • 23. Thank you Connie Chen - @coni Twilio We’re hiring!
  • 24. final case class OptionT[F[_], A](value: F[Option[A]]) { def fold[B](default: => B)(f: A => B)(implicit F: Functor[F]): F[B] = F.map(value)(_.fold(default)(f)) def map[B](f: A => B)(implicit F: Functor[F]): OptionT[F, B] = OptionT(F.map(value)(_.map(f))) def flatMapF[B](f: A => F[Option[B]])(implicit F: Monad[F]): OptionT[F, B] = OptionT(F.flatMap(value)(_.fold(F.pure[Option[B]] (None))(f))) OptionT implementation
  • 25. def liftF[F[_], A](fa: F[A])(implicit F: Functor[F]): OptionT[F, A] = OptionT(F.map(fa) (Some(_))) OptionT implementation