Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Monads in Scala
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Alexey Novakov
March 06, 2020
Programming
130
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Monads in Scala
Monad as an abstract interface. Usage of Monads in Scala
Alexey Novakov
March 06, 2020
More Decks by Alexey Novakov
See All by Alexey Novakov
Особенности разработки Flink джобов на Scala
alexeyn
0
87
Streamhouse Architecture with Flink and Paimon
alexeyn
0
360
Flink Forward 2023: State of Scala API in Apache Flink
alexeyn
0
230
State of Scala API in Apache Flink
alexeyn
0
480
Deployment of Streaming Application with Ververica Platform on Kubernetes
alexeyn
0
290
Rapid Deployment with Apache Flink and Ververica Platform
alexeyn
0
240
Scalacon 2021 - Deep Learning in Scala
alexeyn
0
140
AWS Airflow and EMR
alexeyn
0
160
EMR Data Ingestion with Apache Hudi
alexeyn
0
74
Other Decks in Programming
See All in Programming
ソニーのクラウド共通基盤の変遷とAI時代の開発スタイルに合わせた進化 / The Journey of Sony’s Common Cloud Platform and Its Evolution for AI-Native Development
kenjiyoneyama
0
270
sbt 2.0: takeoff
eed3si9n
0
160
複雑なUIコンポーネントとの ⻑い付き合い⽅:波及するバグからテスト戦略を経て、安全な再設計へ
mixplace
0
410
AIで開発が速くなった先で、開発チームは何を目指すか
shihochan
0
880
R8 の設定見直しでアプリサイズ削減
andpad
0
220
Railsのためのパスキー実践入門 〜マルチテナントSaaSでの意思決定〜
utakaha
2
540
AmebaブログはKMPから卒業します
kmars0605
0
180
ジョジョの世界をXRで再現する
mofurune
0
160
3回つくってわかった、 定期実行ジョブ基盤の本質情報
takai
0
450
動作中のプログラムの中身をリアルタイムに覗く / Realtime Debugger for CSharp with Roslyn
prota
1
1.9k
npm との違いを手がかりにGo の依存管理を理解したい
optim
0
290
全ての永続トークンを、生まれる前に消し去りたい!
sylph01
2
880
Featured
See All Featured
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
2
1.8k
Navigating Weather and Climate Data
rabernat
0
560
Raft: Consensus for Rubyists
vanstee
142
7.8k
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
69
67k
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
1
340
Designing Experiences People Love
moore
143
24k
The Pragmatic Product Professional
lauravandoore
37
7.5k
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
63
46k
How STYLIGHT went responsive
nonsquared
100
6.3k
Statistics for Hackers
jakevdp
799
230k
Building Applications with DynamoDB
mza
96
7.2k
Transcript
Monads in Scala Alexey Novakov, Ultra Tendency, 2020
"A monad is just a monoid in the category of
endofunctors”
“Monad is an abstract interface” Book: Functional Programming in Scala
(Red Book)
Option Either List Future Map Set Stream Vector Future Try
And others … Monads in standard Scala
flatMap unit a.k.a bind a.k.a pure What makes thing a
Monad? “apply” in Scala
class Box[A](v: A) { def flatMap[B](f: A => Box[B]): Box[B]
= f(v) } def map[B](f: A => B): Box[B] = flatMap(a => new Box(f(a))) A Monad modeled as a class minimum set
scala> new Box(1) res2: Box[Int] = 1 scala> res2.map(_ +
1) res3: Box[Int] = 2 scala> res3.flatMap(i => new Box(1 + i)) res5: Box[Int] = 3
scala> val l = List(1,2,3) l: List[Int] = List(1, 2,
3) scala> l.map(_ + 1) res0: List[Int] = List(2, 3, 4) scala> l.flatMap(i => List(i + 1)) res1: List[Int] = List(2, 3, 4) List
val isOn = Some(1) val isBlack = None def makeCoffee:
Option[String] = Some(1) scala> isOn .flatMap(_ => isBlack .flatMap(_ => makeCoffee)) res0: Option[String] = None Option stopped here
trait Functor[F[_]] { def map[A,B](fa: F[A])(f: A => B): F[B]
} trait Monad[F[_]] extends Functor[F] { 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))) } Generic Monad another abstract interface
Monads are like burritos
“f” function application in flatMap & map depends on the
concrete Monad instance “f” applied when: - Option[A]: is Some(A) - Either[A, B]: is Right(B) - List[A]: is non-empty - Future[A]: is ready
Moreover, Monad laws are there
Monad law 1. Identity Example def f(x: Int): Option[Int] =
Some(x) scala> Some(1).flatMap(f) == f(1) res0: Boolean = true scala> f(1) == Some(1).flatMap(f) res1: Boolean = true
Monad law 1. Left Identity def f[A](x: A): Monad[A] =
??? flatMap(unit(x))(f) == f(x) Right Identity f(x) == flatMap(unit(x))(f)
Monad law 2. Associative Example def f1(a: Int): Option[Int] =
Some(a + 1) def f2(a: Int): Option[Int] = Some(a * 2) scala> Some(1).flatMap(f1).flatMap(f2) res0: Option[Int] = Some(4) scala> Some(1).flatMap(a => f1(a).flatMap(f2)) res1: Option[Int] = Some(4)
Monad law 2. Associative def f1[A](a: A): Monad[A] def f2[A](a:
A): Monad[A] if x is a Monad instance, flatMap(flatMap(x)(f1))(f2) == flatMap(x)(a => flatMap(f1(a))(f2))
Functor law 1. Identity map(x)(a => a) == x Example:
map(Some(1))(a => a) == Some(1) the same value returned
Functor law 2. Associative Example: val f1 = (n: Int)
=> n + 1 val f2 = (n: Int) => n * 2 map(map(Some(1))(f1))(f2) // Some(4) == map(Some(1))(f2 compose f1) // Some(4)
Functor law 2. Associative map(map(x)(f1))(f2) == map(x)(f2 compose f1) one
by one apply f1 then f2 in one step
None
final case class Coffee(name: String) val isOn = Some(1) val
coffeeName = Some("black") val makeCoffee = (name: String) => Some(Coffee(name)) for { _ <- isOn name <- coffeeName coffee <- makeCoffee(name) } yield coffee scala> Op)on[Coffee] = Some(Coffee(black)) Compose Option
case class Cluster(pods: Int) def validateNamespace(ns: String): Either[String, Unit] =
Right(()) def clusterExists(ns: String): Either[Cluster, Unit] = Right(()) def createCluster(ns: String, cluster: Cluster): Either[String, Cluster] = Right(Cluster(cluster.pods)) val ns = "my-cluster" for { _ <- validateNamespace(ns) _ <- clusterExists(ns).left.map(c => s"Cluster with ${c.pods} pods already exists") newCluster <- createCluster(ns, Cluster(4)) } yield newCluster Compose Either scala> Either[String,Cluster] = Right(Cluster(4))
scala> Either[String,Cluster] = LeC( Cluster namespace is not valid name,
choose another name ) def validNamespace(ns: String): Either[String, Unit] = if (ns == "my-cluster") Left( “Cluster namespace is not valid name, choose another name” ) else Right(())
for {…} yield is a syntactic sugar for a sequence
of calls: flatMap1(… + flatMapN(.. + map(…))) https://en.wikipedia.org/wiki/Powdered_sugar#/media/File:Powdered_Sugar_-_Macro.jpg
validNamespace("my-cluster") .flatMap(_ => clusterExists(ns) .left .map(c => s"Cluster with ${c.pods}
pods already exists” ).flatMap(_ => createCluster(ns, Cluster(4)) .map(newCluster => newCluster) ) ) Desugared
for { _ <- validNamespace("my-cluster") _ <- clusterExists(ns) .left.map(c =>
s"Cluster with ${c.pods} pods already exists") newCluster <- createCluster(ns, Cluster(4)) } yield newCluster Sugared
Caveat: different Monads do not compose.
def validateNamespace(ns: String): Either[String, Unit] def clusterExists(ns: String): Option[Either[String, Cluster]]
def createCluster(ns: String, cluster: Cluster): Either[String, Cluster] Problem for { _ <- validateNamespace(ns) cluster <- clusterExists(ns) updated <- createCluster(ns, cluster) } yield updated two monad stacks
updated <- createCluster(ns, cluster) ^ <pas)e>:4: error: type mismatch; found
: Either[String,Cluster] required: Cluster cluster <- clusterExists(ns) ^ <pas)e>:3: error: type mismatch; found : Op)on[Nothing] required: scala.u)l.Either[?,?] Op)on[Nothing] <: scala.u)l.Either[?,?]? false
Monad Transformers
- custom-written monad - specifically constructed for composition OptionT: Option
+ Any other Monad EitherT: Either + Any other Monad ReaderT: Reader + Any other Monad WriterT: Writer + Any other Monad … others
EitherT *Example: unwrap second stack * from cats.data.EitherT.scala final case
class EitherT[F[_], A, B](value: F[Either[A, B]]) { def flatMap[AA >: A, D]( f: B => EitherT[F, AA, D])( implicit F: Monad[F]): EitherT[F, AA, D] = EitherT(F.flatMap(value) { case l @ Left(_) => F.pure(l.rightCast) case Right(b) => f(b).value }) }
case class Cluster(pods: Int, updated: Long) def validateNamespace(ns: String): Either[String,
Unit] = Right(()) def clusterExists(ns: String): Option[Either[String, Cluster]] = Some(Right(Cluster(3, System.currentTimeMillis()))) def updateCluster(ns: String, cluster: Cluster): Either[String, Cluster] = Right(Cluster(cluster.pods, System.currentTimeMillis())) Usage
import cats.implicits._ import cats.data.EitherT val cluster = for { _
<- validateNamespace(ns).toEitherT[Option] cluster<- EitherT(clusterExists(ns)) updated <- updateCluster(ns, cluster).toEitherT[Option] } yield updated scala> cluster.value Some(Right(Cluster(3,1583095558496))) EitherT: Either + Option common ground
Error case def clusterExists(ns: String): Option[Either[String, Cluster]] = Left("Cluster is
invalid").some scala> cluster.value res4: Op)on[Either[String,Cluster]] = Some(LeC(Cluster is invalid))
Alexey Novakov email: - alexey.novakov at ultratendency.com - novakov.alex at
gmail.com Blog: https://medium.com/se-notes-by-alexey-novakov https://novakov-alexey.github.io/ Code: https://github.com/novakov-alexey Twitter: @alexey_novakov Thank you! Questions?