SlideShare ist ein Scribd-Unternehmen logo
1 von 67
Downloaden Sie, um offline zu lesen
Beyond Scala Lenses
github: @julien-truffaut or twitter: @JulienTruffaut
Function
S A
A function transforms all s in S into an A
Function
Isomorphism
S A
For all s: S, g(f(s)) == s For all a: A, f(g(a)) == a
f
g
Iso
case class Iso[S,A](
get : S => A,
reverseGet: A => S
)
Properties:
For all s: S, reverseGet(get(s)) == s
For all a: A, get(reverseGet(a)) == a
Modify
A A
f
Modify
A A
S S
f
get
reverseGet
modify
Compose
S A
Iso
B
Iso
Iso
Iso Derived Methods
case class Iso[S,A](
get : S => A,
reverseGet: A => S
){
def modify(m: A => A): S => S
def reverse: Iso[A,S]
def compose[B](other: Iso[A,B]): Iso[S,B]
}
Units
class Robot{
def moveBy(d: Double): Robot
}
val nono: Robot = …
nono.moveBy(100.5) // Meters
nono.moveBy(3) // Kilometers
nono.moveBy(-2.5) // Yards
Safe Unit
case class Meter(d: Double)
case class Yard(d: Double)
class Robot{
def moveBy(m: Meter): Robot
}
nono.moveBy(Meter(100.5))
nono.moveBy(10) // does not compile
nono.moveBy(Yard(3.0)) // does not compile
Iso
Meter Yard
Meter to Yard
val meterToYard: Iso[Meter, Yard] = Iso(
m => Yard(m.value * 1.09361),
y => Meter(y.value / 1.09361)
)
meterToYard.get(Meter(200)) == Yard(218.7219999…)
nono.moveBy(meterToYard.reverseGet(Yard(2.5))
Iso
Meter Yard
Kilometer
Iso
Meter Yard
Kilometer Mile
Iso Composition
case class Kilometer(value: Double)
case class Mile(value: Double)
val meterToKilometer: Iso[Meter, Kilometer] = …
val yardToMile : Iso[Yard, Mile] = …
val kilometerToMile: Iso[Kilometer, Mile] =
meterToKilometer.reverse compose
meterToYard compose
yardToMile
Different Representation
val stringToList: Iso[String, List[Char]] =
Iso(_.toList, _.mkString(“”))
def listToVector[A]: Iso[List[A], Vector[A]] =
Iso(_.toVector, _.toList)
Generic Programming
case object Red
val red: Iso[Red, Unit] = Iso(Red => (), () => Red)
case class Person(name: String, age: Int)
val personToTuple: Iso[Person, (String, Int)] = …
Iso Properties
For all s: S, reverseGet(get(s)) == s
For all a: A, get(reverseGet(a)) == a
Scalacheck
def isoLaws[S,A](iso: Iso[S,A]) = new Properties {
property(“One Way”) = forAll { s: S =>
iso.reverseGet(iso.get(s)) == s
}
property(“Other Way”) = forAll { a: A =>
iso.get(iso.reverseGet(a)) == a
}
}
Scalacheck
import org.spec2.scalaz.Spec
class IsoSpec extends Spec {
checkAll(“meter to yard”, isoLaws(meterToYard))
}
val meterToYard: Iso[Meter, Yard] = Iso(
m => Yard(m.value * 1.09361),
y => Meter(y.value / 1.09361)
)
Scalacheck
A counter-example is:
[Yard(1.518707721E9)] (after 24 tries)
scala> meterToYard.get(meterToYard.reverseGet(
Yard(1.518707721E9)))
scala> res0: Yard= Yard(1.5187077210000002E9)
Relaxed Isomorphism
S
A
For all s: S such as f(s) exists, g(f(s)) == s
For all a: A, f(g(a)) == a
?
f
g
f is a Function[S, Option[A]]
g is a Function[A, S]
Prism
case class Prism[S,A](
getOption : S => Option[A],
reverseGet: A => S
)
Properties:
For all s: S, getOption(s) map reverseGet == Some(s) || None
For all a: A, getOption(reverseGet(a)) == Some(a)
Pattern matching / Constructor
sealed trait List[A]
case class Cons[A](h: A, t: List[A]) extends List[A]
case class Nil[A]() extends List[A]
Cons.unapply(List(1,2,3)) == Some((1, List(2,3)))
Cons.unapply(Nil) == None
Cons.apply(1, List(2,3)) == List(1,2,3)
Prism
sealed trait List[A]
case class Cons[A](h: A, t: List[A]) extends List[A]
case class Nil[A]() extends List[A]
def cons[A]: Prism[List[A], (A, List[A])] = …
cons.getOption(List(1,2,3)) == Some((1, List(2,3)))
cons.getOption(Nil) == None
cons.reverseGet(1, List(2,3)) == List(1,2,3)
Prism Derived Methods
case class Prism[S,A](
getOption: S => Option[A],
reverseGet: A => S
){
def isMatching(s: S): Boolean
def modify(f: A => A): S=> S
def modifyOption(f: A => A): S => Option[S]
def compose[B](other: Prism[A,B]): Prism[S,B]
def compose[B](other: Iso[A,B]): ???[S,B]
}
Iso – Prism
Optic f g
Iso S => A A => S
Prism S => Option[A] A => S
def isoToPrism[S,A](iso: Iso[S,A]): Prism[S,A] =
Prism(
getOption = s => Some(iso.get(s)),
reverseGet = iso.reverseGet
)
Iso – Prism
case class Prism[S,A]{
def compose[B](other: Prism[A,B]): Prism[S,B]
def compose[B](other: Iso[A,B]): Prism[S,B]
}
case class Iso[S,A]{
def compose[B](other: Iso[A,B]): Iso[S,B]
def compose[B](other: Prism[A,B]): Prism[S,B]
}
Enum
sealed trait Day
case object Monday extends Day
case object Tuesday extends Day
val tuesday: Prism[Day, Unit] = …
tuesday.getOption(Monday) == None
tuesday.getOption(Tuesday) == Some(())
tuesday.reverseGet(()) == Tuesday
Json
sealed trait Json
case class JNumber(v: Double) extends Json
case class JString(s: String) extends Json
val jNum: Prism[Json, Double] = …
jNum.modify(_ + 1)(JNumber(2.0)) == JNumber(3.0)
jNum.modify(_ + 1)(JString(“”)) == JString(“”)
jNum.modifyOption(_ + 1)(JString(“”)) == None
Safe Down Casting
val doubleToInt: Prism[Double, Int] = …
doubleToInt.getOption(3.4) == None
doubleToInt.getOption(3.0) == Some(3)
doubleToInt.reverseGet(5) == 5.0
Prism Composition
sealed trait Json
case class JNumber(v: Double) extends Json
case class JString(s: String) extends Json
val jInt: Prism[Json, Int] = jNum compose doubleToInt
jInt.getOption(JNumber(3.0)) == Some(3)
jInt.getOption(JNumber(5.9)) == None
jInt.getOption(JString(“”)) == None
Where is the bug?
def stringToInt: Prism[String, Int] = Prism(
getOption = s => Try(s.toInt).toOption,
reverseGet = _.toString
)
stringToInt.modify(_ * 2)(“5”) == “10”
stringToInt.getOption(“5”) == Some(5)
stringToInt.getOption(“-3”) == Some(-3)
stringToInt.getOption(“5.7”) == None
stringToInt.getOption(“99999999999999999”) == None
stringToInt.getOption(“Hello”) == None
Tadam !
” .toInt = 9“
Prism
S A B COr Or
sealed trait S
class A extends S
class B extends S
class C extends S
Lens
S A B CAnd And
case class S(a: A, b: B, c: C)
Lens
case class Lens[S,A](
get: S => A,
set:(A, S) => S
)
Properties:
For all s: S, set(get(s), s) == s
For all a: A, s: S, get(set(a, s)) == a
Iso – Lens
Optic f g
Iso S => A A => S
Lens S => A (A, S) => S
def isoToLens[S,A](iso: Iso[S,A]): Lens[S,A] =
Lens(
get = iso.get,
set = (a, _) => iso.reverseGet(a)
)
Accessors
case class Person(name: String, age: Int)
val age = Lens[Person, Int](_.age, (a, p) => p.copy(age = a))
val zoe = Person(“Zoe”, 25)
age.get(zoe) == 25
age.set(20, zoe) == Person(“Zoe”, 20)
age.modify(_ + 1)(zoe) == Person(“Zoe”, 26)
Nested Accessors
case class Person(name: String, age: Int, address: Address)
case class Address(streetNumber: Int, StreetName: String)
val address: Lens[Person, Address] = …
val streetName: Lens[Address, String] = …
val zoe = Person(“Zoe”, 25, Address(10, “High Street”))
(address compose streetName).get(zoe) == “High Street”
(address compose streetName).set(“Iffley Road”, zoe)
== Person(“Zoe”, 25, Address(10, “Iffley Road”))
Tuples
def first[A,B]: Lens[(A,B), A] = …
def second[A,B]: Lens[(A,B), B] = …
val tuple = (2,“Zoe”)
first.set(4, tuple) == (4,“Zoe”)
second.modify(_.reverse)(tuple) == (2,“eoZ”)
Universal Case Class Accessors
case class Person(name: String, age: Int)
val personToTuple: Iso[Person,(String,Int)] = …
val zoe = Person(“Zoe”, 25)
(personToTuple compose second).set(30, zoe)
== Person(“Zoe”, 30)
At
def at[K,V](k: K): Lens[Map[K,V], Option[V]] =
Lens(
get = m => m.get(k),
set = (optV, m) => optV match {
case None => m – k
case Some(v)=> m + (k -> v)
}
)
Map
1“one”
2“two”
1“one”
at(“three”).set(Some(3), m)
1“one”
2“two”
at(“two”).set(None, m)
3“three”
What Next?
Optic f g
Iso S => A A => S
Prism S => Option[A] A => S
Lens S => A (A, S) => S
Optional
Optic f g
Iso S => A A => S
Prism S => Option[A] A => S
Lens S => A (A, S) => S
Optional S => Option[A] (A, S) => S
Optional
PrismLens
Iso
Optional
case class Optional[S,A](
getOption: S => Option[A],
set :(A, S) => S
)
Properties:
For all s: S, getOption(s) map set(_, s) == Some(s)
For all a: A, s: S, getOption(set(a, s)) == Some(a) || None
Head
def cons[A]: Prism[List[A], (A, List[A])] = …
def first[A, B]: Lens[(A, B), A] = …
def head[A]: Optional[List[A], A] = cons compose first
head.getOption(List(1,2,3)) = Some(1)
head.set(0, List(1,2,3)) = List(0,2,3)
Void
def void[S,A]: Optional[S, A] =
Optional(
getOption = s => None,
set = (a, s) => s
)
void.getOption(“Hello”) = None
void.set(1,“Hello”) = “Hello”
void.setOption(1,“Hello”) = None
Index
def index[A](i: Int): Optional[List[A], A] =
if(i < 0) void
else if(i == 0) head
else cons compose second compose index(i – 1)
index(-1).getOption(List(1,2,3)) == None
index(1).getOption(List(1,2,3)) == Some(2)
index(5).getOption(List(1,2,3)) == None
index(1).set(10, List(1,2,3)) == List(1,10,3)
Index for Vector
def vectorToList[A]: Iso[Vector[A], List[A]] = …
def indexV(i: Int): Optional[Vector[A], A] =
vectorToList compose index(i)
Index != At
3 5 6 2
0 1 2 3
3 5 99 2
0 1 2 3
index(2).set(99, l)
val l = List(3,5,6,2)
3 5 6 2 ? ? ? 99
0 1 2 3 4 5 6 7
3 5 6 2
0 1 2 3
index(7).set(99, l)
Study Case: Http Request
sealed trait Method
case object GET extends Method
case object POST extends Method
case class URI(
host: String, port: Int,
path: String, query: Map[String, String]
)
case class Request(
method: Method, uri: URI,
headers: Map[String, String], body: String
)
Study Case: Http Request
val r = Request(
method = GET,
uri = URI(“localhost”,8080,“/ping”,Map(“age”->“15”)),
headers = Map.empty,
body = “”
)
Which method?
val method: Lens[Request, Method] = …
val GET: Prism[Method, Unit] = …
method.get(r) // GET
(method compose GET).isMatching(r) // true
How to update host?
val uri: Lens[Request, URI] = …
val host: Lens[URI, String] = …
(uri compose host).set(“foo.io”)(r)
Request(
method = GET,
uri = URI(“foo.io”,8080,“/ping”,Map(“age”->“15”)),
headers = Map.empty,
body = “”
)
How to increase age query?
val query: Lens[URI, Map[String, String]] = …
(uri compose query compose
index(“age”) compose stringToInt
).modify(_ + 1)(r)
Request(
method = GET,
uri = URI(“localhost”,8080,“/ping”,Map(“age”->“16”)),
headers = Map.empty,
body = “”
)
How to add header?
val headers: Lens[Request, Map[String, String]] = …
(headers compose at(“Content-Length”)).set(Some(“0”))(r)
Request(
method = GET,
uri = URI(“localhost”,8080,“/ping”,Map(“age”->“15”)),
headers = Map(“Content-Length”->“0”),
body = “”
)
More power!!!
val r2 = Request(
method = POST,
uri = URI(“localhost”,8080,“/pong”,Map.empty),
headers = Map(“x-custom1”->“5”, “x-custom2”->“10”),
body = “Hello World”
)
Traversal
(headers compose
filterIndex(_.startsWith(“x-”)) compose
stringToInt
).modify(_ * 2)(r2)
Request(
method = POST,
uri = URI(“localhost”,8080,“/pong”,Map.empty),
headers = Map(“x-custom1”->“10”, “x-custom2”->“20”),
body = “Hello World”
)
Optional
PrismLens
Iso
Traversal
Setter
Getter
Fold
Monocle goodies
▪ Provides lots of built-in optics and functions
▪ Macros for creating Lenses, Iso and Prism
▪ Syntax to use optics as infix operator
▪ Experimental state support
Resources
▪ Monocle on github
▪ Simon Peyton Jones’s lens talk at Scala Exchange 2013
▪ Edward Kmett on Lenses with the State Monad
Acknowledgement
▪ Member Monocle and Cats gitter channel for advice and
review
▪ Special thanks to Ilan Godik (@NightRa) for helping with
slides and content
Thank you!

Weitere ähnliche Inhalte

Was ist angesagt?

Domain Modeling Made Functional (DevTernity 2022)
Domain Modeling Made Functional (DevTernity 2022)Domain Modeling Made Functional (DevTernity 2022)
Domain Modeling Made Functional (DevTernity 2022)Scott Wlaschin
 
Introduction to Category Theory for software engineers
Introduction to Category Theory for software engineersIntroduction to Category Theory for software engineers
Introduction to Category Theory for software engineersPranav Prakash
 
Folding Unfolded - Polyglot FP for Fun and Profit - Haskell and Scala - with ...
Folding Unfolded - Polyglot FP for Fun and Profit - Haskell and Scala - with ...Folding Unfolded - Polyglot FP for Fun and Profit - Haskell and Scala - with ...
Folding Unfolded - Polyglot FP for Fun and Profit - Haskell and Scala - with ...Philip Schwarz
 
1.7. eqivalence of nfa and dfa
1.7. eqivalence of nfa and dfa1.7. eqivalence of nfa and dfa
1.7. eqivalence of nfa and dfaSampath Kumar S
 
Cours DEA Satisfaction et Optimisation sous Contraintes
Cours DEA Satisfaction et Optimisation sous ContraintesCours DEA Satisfaction et Optimisation sous Contraintes
Cours DEA Satisfaction et Optimisation sous Contraintesanonymousmega02
 
A CGRA-based Approach for Accelerating Convolutional Neural Networks
A CGRA-based Approachfor Accelerating Convolutional Neural NetworksA CGRA-based Approachfor Accelerating Convolutional Neural Networks
A CGRA-based Approach for Accelerating Convolutional Neural NetworksShinya Takamaeda-Y
 
Domain Modeling with FP (DDD Europe 2020)
Domain Modeling with FP (DDD Europe 2020)Domain Modeling with FP (DDD Europe 2020)
Domain Modeling with FP (DDD Europe 2020)Scott Wlaschin
 
プロフェッショナルSSL/TLS 7章
プロフェッショナルSSL/TLS 7章プロフェッショナルSSL/TLS 7章
プロフェッショナルSSL/TLS 7章MITSUNARI Shigeo
 
Sequence and Traverse - Part 1
Sequence and Traverse - Part 1Sequence and Traverse - Part 1
Sequence and Traverse - Part 1Philip Schwarz
 
Functional and Algebraic Domain Modeling
Functional and Algebraic Domain ModelingFunctional and Algebraic Domain Modeling
Functional and Algebraic Domain ModelingDebasish Ghosh
 
ソフトシンセを作りながら学ぶPythonプログラミング
ソフトシンセを作りながら学ぶPythonプログラミングソフトシンセを作りながら学ぶPythonプログラミング
ソフトシンセを作りながら学ぶPythonプログラミングRansui Iso
 
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...Philip Schwarz
 
Depth First Search ( DFS )
Depth First Search ( DFS )Depth First Search ( DFS )
Depth First Search ( DFS )Sazzad Hossain
 
高速フーリエ変換
高速フーリエ変換高速フーリエ変換
高速フーリエ変換AtCoder Inc.
 
すごいHaskell 第7章 型や型クラスを自分で作ろう(前編)
すごいHaskell 第7章 型や型クラスを自分で作ろう(前編)すごいHaskell 第7章 型や型クラスを自分で作ろう(前編)
すごいHaskell 第7章 型や型クラスを自分で作ろう(前編)Nozomu Kaneko
 
Kotlin as a Better Java
Kotlin as a Better JavaKotlin as a Better Java
Kotlin as a Better JavaGarth Gilmour
 

Was ist angesagt? (20)

Domain Modeling Made Functional (DevTernity 2022)
Domain Modeling Made Functional (DevTernity 2022)Domain Modeling Made Functional (DevTernity 2022)
Domain Modeling Made Functional (DevTernity 2022)
 
Introduction to Category Theory for software engineers
Introduction to Category Theory for software engineersIntroduction to Category Theory for software engineers
Introduction to Category Theory for software engineers
 
Folding Unfolded - Polyglot FP for Fun and Profit - Haskell and Scala - with ...
Folding Unfolded - Polyglot FP for Fun and Profit - Haskell and Scala - with ...Folding Unfolded - Polyglot FP for Fun and Profit - Haskell and Scala - with ...
Folding Unfolded - Polyglot FP for Fun and Profit - Haskell and Scala - with ...
 
1.7. eqivalence of nfa and dfa
1.7. eqivalence of nfa and dfa1.7. eqivalence of nfa and dfa
1.7. eqivalence of nfa and dfa
 
Cours DEA Satisfaction et Optimisation sous Contraintes
Cours DEA Satisfaction et Optimisation sous ContraintesCours DEA Satisfaction et Optimisation sous Contraintes
Cours DEA Satisfaction et Optimisation sous Contraintes
 
A CGRA-based Approach for Accelerating Convolutional Neural Networks
A CGRA-based Approachfor Accelerating Convolutional Neural NetworksA CGRA-based Approachfor Accelerating Convolutional Neural Networks
A CGRA-based Approach for Accelerating Convolutional Neural Networks
 
Why rust?
Why rust?Why rust?
Why rust?
 
F# for C# Programmers
F# for C# ProgrammersF# for C# Programmers
F# for C# Programmers
 
Domain Modeling with FP (DDD Europe 2020)
Domain Modeling with FP (DDD Europe 2020)Domain Modeling with FP (DDD Europe 2020)
Domain Modeling with FP (DDD Europe 2020)
 
プロフェッショナルSSL/TLS 7章
プロフェッショナルSSL/TLS 7章プロフェッショナルSSL/TLS 7章
プロフェッショナルSSL/TLS 7章
 
Idiomatic kotlin
Idiomatic kotlinIdiomatic kotlin
Idiomatic kotlin
 
Sequence and Traverse - Part 1
Sequence and Traverse - Part 1Sequence and Traverse - Part 1
Sequence and Traverse - Part 1
 
Functional and Algebraic Domain Modeling
Functional and Algebraic Domain ModelingFunctional and Algebraic Domain Modeling
Functional and Algebraic Domain Modeling
 
Push Down Automata (PDA)
Push Down Automata (PDA)Push Down Automata (PDA)
Push Down Automata (PDA)
 
ソフトシンセを作りながら学ぶPythonプログラミング
ソフトシンセを作りながら学ぶPythonプログラミングソフトシンセを作りながら学ぶPythonプログラミング
ソフトシンセを作りながら学ぶPythonプログラミング
 
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
 
Depth First Search ( DFS )
Depth First Search ( DFS )Depth First Search ( DFS )
Depth First Search ( DFS )
 
高速フーリエ変換
高速フーリエ変換高速フーリエ変換
高速フーリエ変換
 
すごいHaskell 第7章 型や型クラスを自分で作ろう(前編)
すごいHaskell 第7章 型や型クラスを自分で作ろう(前編)すごいHaskell 第7章 型や型クラスを自分で作ろう(前編)
すごいHaskell 第7章 型や型クラスを自分で作ろう(前編)
 
Kotlin as a Better Java
Kotlin as a Better JavaKotlin as a Better Java
Kotlin as a Better Java
 

Andere mochten auch

Optics with monocle - Modeling the part and the whole
Optics with monocle - Modeling the part and the wholeOptics with monocle - Modeling the part and the whole
Optics with monocle - Modeling the part and the wholeIlan Godik
 
Scala lens: An introduction
Scala lens: An introductionScala lens: An introduction
Scala lens: An introductionKnoldus Inc.
 
Neural Network as a function
Neural Network as a functionNeural Network as a function
Neural Network as a functionTaisuke Oe
 
Monocleとかいうのがありまして
MonocleとかいうのがありましてMonocleとかいうのがありまして
MonocleとかいうのがありましてNaoki Aoyama
 
Exploring the Scala ecosystem
Exploring the Scala ecosystemExploring the Scala ecosystem
Exploring the Scala ecosystemDenis
 
Demystifying functional programming with Scala
Demystifying functional programming with ScalaDemystifying functional programming with Scala
Demystifying functional programming with ScalaDenis
 
From functional to Reactive - patterns in domain modeling
From functional to Reactive - patterns in domain modelingFrom functional to Reactive - patterns in domain modeling
From functional to Reactive - patterns in domain modelingDebasish Ghosh
 
Ekmett勉強会発表資料
Ekmett勉強会発表資料Ekmett勉強会発表資料
Ekmett勉強会発表資料時響 逢坂
 
RESTful API using scalaz (3)
RESTful API using scalaz (3)RESTful API using scalaz (3)
RESTful API using scalaz (3)Yeshwanth Kumar
 
Purely Functional Data Structures in Scala
Purely Functional Data Structures in ScalaPurely Functional Data Structures in Scala
Purely Functional Data Structures in ScalaVladimir Kostyukov
 
Scalaで型クラス入門
Scalaで型クラス入門Scalaで型クラス入門
Scalaで型クラス入門Makoto Fukuhara
 
Van laarhoven lens
Van laarhoven lensVan laarhoven lens
Van laarhoven lensNaoki Aoyama
 
Concurrent Mark-Sweep Garbage Collection #jjug_ccc
Concurrent Mark-Sweep Garbage Collection #jjug_cccConcurrent Mark-Sweep Garbage Collection #jjug_ccc
Concurrent Mark-Sweep Garbage Collection #jjug_cccYuji Kubota
 
Reducing Boilerplate and Combining Effects: A Monad Transformer Example
Reducing Boilerplate and Combining Effects: A Monad Transformer ExampleReducing Boilerplate and Combining Effects: A Monad Transformer Example
Reducing Boilerplate and Combining Effects: A Monad Transformer ExampleConnie Chen
 
Scalaz By Example (An IO Taster) -- PDXScala Meetup Jan 2014
Scalaz By Example (An IO Taster) -- PDXScala Meetup Jan 2014Scalaz By Example (An IO Taster) -- PDXScala Meetup Jan 2014
Scalaz By Example (An IO Taster) -- PDXScala Meetup Jan 2014Susan Potter
 
Make your programs Free
Make your programs FreeMake your programs Free
Make your programs FreePawel Szulc
 
λ | Lenses
λ | Lensesλ | Lenses
λ | LensesOpen-IT
 

Andere mochten auch (19)

Optics with monocle - Modeling the part and the whole
Optics with monocle - Modeling the part and the wholeOptics with monocle - Modeling the part and the whole
Optics with monocle - Modeling the part and the whole
 
Scala lens: An introduction
Scala lens: An introductionScala lens: An introduction
Scala lens: An introduction
 
Neural Network as a function
Neural Network as a functionNeural Network as a function
Neural Network as a function
 
Monocleとかいうのがありまして
MonocleとかいうのがありましてMonocleとかいうのがありまして
Monocleとかいうのがありまして
 
Exploring the Scala ecosystem
Exploring the Scala ecosystemExploring the Scala ecosystem
Exploring the Scala ecosystem
 
Demystifying functional programming with Scala
Demystifying functional programming with ScalaDemystifying functional programming with Scala
Demystifying functional programming with Scala
 
From functional to Reactive - patterns in domain modeling
From functional to Reactive - patterns in domain modelingFrom functional to Reactive - patterns in domain modeling
From functional to Reactive - patterns in domain modeling
 
Ekmett勉強会発表資料
Ekmett勉強会発表資料Ekmett勉強会発表資料
Ekmett勉強会発表資料
 
Xpath in-lens
Xpath in-lensXpath in-lens
Xpath in-lens
 
RESTful API using scalaz (3)
RESTful API using scalaz (3)RESTful API using scalaz (3)
RESTful API using scalaz (3)
 
Purely Functional Data Structures in Scala
Purely Functional Data Structures in ScalaPurely Functional Data Structures in Scala
Purely Functional Data Structures in Scala
 
Scalaで型クラス入門
Scalaで型クラス入門Scalaで型クラス入門
Scalaで型クラス入門
 
Van laarhoven lens
Van laarhoven lensVan laarhoven lens
Van laarhoven lens
 
Concurrent Mark-Sweep Garbage Collection #jjug_ccc
Concurrent Mark-Sweep Garbage Collection #jjug_cccConcurrent Mark-Sweep Garbage Collection #jjug_ccc
Concurrent Mark-Sweep Garbage Collection #jjug_ccc
 
Reducing Boilerplate and Combining Effects: A Monad Transformer Example
Reducing Boilerplate and Combining Effects: A Monad Transformer ExampleReducing Boilerplate and Combining Effects: A Monad Transformer Example
Reducing Boilerplate and Combining Effects: A Monad Transformer Example
 
Scalaz By Example (An IO Taster) -- PDXScala Meetup Jan 2014
Scalaz By Example (An IO Taster) -- PDXScala Meetup Jan 2014Scalaz By Example (An IO Taster) -- PDXScala Meetup Jan 2014
Scalaz By Example (An IO Taster) -- PDXScala Meetup Jan 2014
 
Make your programs Free
Make your programs FreeMake your programs Free
Make your programs Free
 
Embulk 20150411
Embulk 20150411Embulk 20150411
Embulk 20150411
 
λ | Lenses
λ | Lensesλ | Lenses
λ | Lenses
 

Ähnlich wie Beyond Scala Lens

Type classes 101 - classification beyond inheritance
Type classes 101 - classification beyond inheritanceType classes 101 - classification beyond inheritance
Type classes 101 - classification beyond inheritanceAlexey Raga
 
Scala Functional Patterns
Scala Functional PatternsScala Functional Patterns
Scala Functional Patternsleague
 
The Essence of the Iterator Pattern
The Essence of the Iterator PatternThe Essence of the Iterator Pattern
The Essence of the Iterator PatternEric Torreborre
 
Monadologie
MonadologieMonadologie
Monadologieleague
 
The Essence of the Iterator Pattern (pdf)
The Essence of the Iterator Pattern (pdf)The Essence of the Iterator Pattern (pdf)
The Essence of the Iterator Pattern (pdf)Eric Torreborre
 
関数潮流(Function Tendency)
関数潮流(Function Tendency)関数潮流(Function Tendency)
関数潮流(Function Tendency)riue
 
Elixir -Tolerância a Falhas para Adultos - GDG Campinas
Elixir  -Tolerância a Falhas para Adultos - GDG CampinasElixir  -Tolerância a Falhas para Adultos - GDG Campinas
Elixir -Tolerância a Falhas para Adultos - GDG CampinasFabio Akita
 
ZIO actors by Mateusz Sokół Scalac
ZIO actors by Mateusz Sokół ScalacZIO actors by Mateusz Sokół Scalac
ZIO actors by Mateusz Sokół ScalacScalac
 
Fp in scala with adts part 2
Fp in scala with adts part 2Fp in scala with adts part 2
Fp in scala with adts part 2Hang Zhao
 
Hitchhiker's Guide to Functional Programming
Hitchhiker's Guide to Functional ProgrammingHitchhiker's Guide to Functional Programming
Hitchhiker's Guide to Functional ProgrammingSergey Shishkin
 
Swift 함수 커링 사용하기
Swift 함수 커링 사용하기Swift 함수 커링 사용하기
Swift 함수 커링 사용하기진성 오
 
ES6 and AngularAMD
ES6 and AngularAMDES6 and AngularAMD
ES6 and AngularAMDdhaval10690
 

Ähnlich wie Beyond Scala Lens (20)

Type classes 101 - classification beyond inheritance
Type classes 101 - classification beyond inheritanceType classes 101 - classification beyond inheritance
Type classes 101 - classification beyond inheritance
 
Functional optics
Functional opticsFunctional optics
Functional optics
 
Scala Functional Patterns
Scala Functional PatternsScala Functional Patterns
Scala Functional Patterns
 
The Essence of the Iterator Pattern
The Essence of the Iterator PatternThe Essence of the Iterator Pattern
The Essence of the Iterator Pattern
 
Monadologie
MonadologieMonadologie
Monadologie
 
Functional programming in scala
Functional programming in scalaFunctional programming in scala
Functional programming in scala
 
Scala best practices
Scala best practicesScala best practices
Scala best practices
 
The Essence of the Iterator Pattern (pdf)
The Essence of the Iterator Pattern (pdf)The Essence of the Iterator Pattern (pdf)
The Essence of the Iterator Pattern (pdf)
 
関数潮流(Function Tendency)
関数潮流(Function Tendency)関数潮流(Function Tendency)
関数潮流(Function Tendency)
 
Elixir -Tolerância a Falhas para Adultos - GDG Campinas
Elixir  -Tolerância a Falhas para Adultos - GDG CampinasElixir  -Tolerância a Falhas para Adultos - GDG Campinas
Elixir -Tolerância a Falhas para Adultos - GDG Campinas
 
07012023.pptx
07012023.pptx07012023.pptx
07012023.pptx
 
ZIO actors by Mateusz Sokół Scalac
ZIO actors by Mateusz Sokół ScalacZIO actors by Mateusz Sokół Scalac
ZIO actors by Mateusz Sokół Scalac
 
Miracle of std lib
Miracle of std libMiracle of std lib
Miracle of std lib
 
Scala by Luc Duponcheel
Scala by Luc DuponcheelScala by Luc Duponcheel
Scala by Luc Duponcheel
 
Fp in scala with adts part 2
Fp in scala with adts part 2Fp in scala with adts part 2
Fp in scala with adts part 2
 
Hitchhiker's Guide to Functional Programming
Hitchhiker's Guide to Functional ProgrammingHitchhiker's Guide to Functional Programming
Hitchhiker's Guide to Functional Programming
 
Swift 함수 커링 사용하기
Swift 함수 커링 사용하기Swift 함수 커링 사용하기
Swift 함수 커링 사용하기
 
2014-11-01 01 Денис Нелюбин. О сортах кофе
2014-11-01 01 Денис Нелюбин. О сортах кофе2014-11-01 01 Денис Нелюбин. О сортах кофе
2014-11-01 01 Денис Нелюбин. О сортах кофе
 
SDC - Einführung in Scala
SDC - Einführung in ScalaSDC - Einführung in Scala
SDC - Einführung in Scala
 
ES6 and AngularAMD
ES6 and AngularAMDES6 and AngularAMD
ES6 and AngularAMD
 

Kürzlich hochgeladen

Enterprise Document Management System - Qualityze Inc
Enterprise Document Management System - Qualityze IncEnterprise Document Management System - Qualityze Inc
Enterprise Document Management System - Qualityze Incrobinwilliams8624
 
Generative AI for Cybersecurity - EC-Council
Generative AI for Cybersecurity - EC-CouncilGenerative AI for Cybersecurity - EC-Council
Generative AI for Cybersecurity - EC-CouncilVICTOR MAESTRE RAMIREZ
 
Leveraging DxSherpa's Generative AI Services to Unlock Human-Machine Harmony
Leveraging DxSherpa's Generative AI Services to Unlock Human-Machine HarmonyLeveraging DxSherpa's Generative AI Services to Unlock Human-Machine Harmony
Leveraging DxSherpa's Generative AI Services to Unlock Human-Machine Harmonyelliciumsolutionspun
 
ERP For Electrical and Electronics manufecturing.pptx
ERP For Electrical and Electronics manufecturing.pptxERP For Electrical and Electronics manufecturing.pptx
ERP For Electrical and Electronics manufecturing.pptxAutus Cyber Tech
 
online pdf editor software solutions.pdf
online pdf editor software solutions.pdfonline pdf editor software solutions.pdf
online pdf editor software solutions.pdfMeon Technology
 
eAuditor Audits & Inspections - conduct field inspections
eAuditor Audits & Inspections - conduct field inspectionseAuditor Audits & Inspections - conduct field inspections
eAuditor Audits & Inspections - conduct field inspectionsNirav Modi
 
How Does the Epitome of Spyware Differ from Other Malicious Software?
How Does the Epitome of Spyware Differ from Other Malicious Software?How Does the Epitome of Spyware Differ from Other Malicious Software?
How Does the Epitome of Spyware Differ from Other Malicious Software?AmeliaSmith90
 
Why Choose Brain Inventory For Ecommerce Development.pdf
Why Choose Brain Inventory For Ecommerce Development.pdfWhy Choose Brain Inventory For Ecommerce Development.pdf
Why Choose Brain Inventory For Ecommerce Development.pdfBrain Inventory
 
Kawika Technologies pvt ltd Software Development Company in Trivandrum
Kawika Technologies pvt ltd Software Development Company in TrivandrumKawika Technologies pvt ltd Software Development Company in Trivandrum
Kawika Technologies pvt ltd Software Development Company in TrivandrumKawika Technologies
 
Sales Territory Management: A Definitive Guide to Expand Sales Coverage
Sales Territory Management: A Definitive Guide to Expand Sales CoverageSales Territory Management: A Definitive Guide to Expand Sales Coverage
Sales Territory Management: A Definitive Guide to Expand Sales CoverageDista
 
OpenChain Webinar: Universal CVSS Calculator
OpenChain Webinar: Universal CVSS CalculatorOpenChain Webinar: Universal CVSS Calculator
OpenChain Webinar: Universal CVSS CalculatorShane Coughlan
 
Streamlining Your Application Builds with Cloud Native Buildpacks
Streamlining Your Application Builds  with Cloud Native BuildpacksStreamlining Your Application Builds  with Cloud Native Buildpacks
Streamlining Your Application Builds with Cloud Native BuildpacksVish Abrams
 
Big Data Bellevue Meetup | Enhancing Python Data Loading in the Cloud for AI/ML
Big Data Bellevue Meetup | Enhancing Python Data Loading in the Cloud for AI/MLBig Data Bellevue Meetup | Enhancing Python Data Loading in the Cloud for AI/ML
Big Data Bellevue Meetup | Enhancing Python Data Loading in the Cloud for AI/MLAlluxio, Inc.
 
Cybersecurity Challenges with Generative AI - for Good and Bad
Cybersecurity Challenges with Generative AI - for Good and BadCybersecurity Challenges with Generative AI - for Good and Bad
Cybersecurity Challenges with Generative AI - for Good and BadIvo Andreev
 
Top Software Development Trends in 2024
Top Software Development Trends in  2024Top Software Development Trends in  2024
Top Software Development Trends in 2024Mind IT Systems
 
JS-Experts - Cybersecurity for Generative AI
JS-Experts - Cybersecurity for Generative AIJS-Experts - Cybersecurity for Generative AI
JS-Experts - Cybersecurity for Generative AIIvo Andreev
 
ARM Talk @ Rejekts - Will ARM be the new Mainstream in our Data Centers_.pdf
ARM Talk @ Rejekts - Will ARM be the new Mainstream in our Data Centers_.pdfARM Talk @ Rejekts - Will ARM be the new Mainstream in our Data Centers_.pdf
ARM Talk @ Rejekts - Will ARM be the new Mainstream in our Data Centers_.pdfTobias Schneck
 
Webinar_050417_LeClair12345666777889.ppt
Webinar_050417_LeClair12345666777889.pptWebinar_050417_LeClair12345666777889.ppt
Webinar_050417_LeClair12345666777889.pptkinjal48
 
Your Vision, Our Expertise: TECUNIQUE's Tailored Software Teams
Your Vision, Our Expertise: TECUNIQUE's Tailored Software TeamsYour Vision, Our Expertise: TECUNIQUE's Tailored Software Teams
Your Vision, Our Expertise: TECUNIQUE's Tailored Software TeamsJaydeep Chhasatia
 

Kürzlich hochgeladen (20)

Enterprise Document Management System - Qualityze Inc
Enterprise Document Management System - Qualityze IncEnterprise Document Management System - Qualityze Inc
Enterprise Document Management System - Qualityze Inc
 
Generative AI for Cybersecurity - EC-Council
Generative AI for Cybersecurity - EC-CouncilGenerative AI for Cybersecurity - EC-Council
Generative AI for Cybersecurity - EC-Council
 
Leveraging DxSherpa's Generative AI Services to Unlock Human-Machine Harmony
Leveraging DxSherpa's Generative AI Services to Unlock Human-Machine HarmonyLeveraging DxSherpa's Generative AI Services to Unlock Human-Machine Harmony
Leveraging DxSherpa's Generative AI Services to Unlock Human-Machine Harmony
 
ERP For Electrical and Electronics manufecturing.pptx
ERP For Electrical and Electronics manufecturing.pptxERP For Electrical and Electronics manufecturing.pptx
ERP For Electrical and Electronics manufecturing.pptx
 
online pdf editor software solutions.pdf
online pdf editor software solutions.pdfonline pdf editor software solutions.pdf
online pdf editor software solutions.pdf
 
eAuditor Audits & Inspections - conduct field inspections
eAuditor Audits & Inspections - conduct field inspectionseAuditor Audits & Inspections - conduct field inspections
eAuditor Audits & Inspections - conduct field inspections
 
How Does the Epitome of Spyware Differ from Other Malicious Software?
How Does the Epitome of Spyware Differ from Other Malicious Software?How Does the Epitome of Spyware Differ from Other Malicious Software?
How Does the Epitome of Spyware Differ from Other Malicious Software?
 
Why Choose Brain Inventory For Ecommerce Development.pdf
Why Choose Brain Inventory For Ecommerce Development.pdfWhy Choose Brain Inventory For Ecommerce Development.pdf
Why Choose Brain Inventory For Ecommerce Development.pdf
 
Kawika Technologies pvt ltd Software Development Company in Trivandrum
Kawika Technologies pvt ltd Software Development Company in TrivandrumKawika Technologies pvt ltd Software Development Company in Trivandrum
Kawika Technologies pvt ltd Software Development Company in Trivandrum
 
Sales Territory Management: A Definitive Guide to Expand Sales Coverage
Sales Territory Management: A Definitive Guide to Expand Sales CoverageSales Territory Management: A Definitive Guide to Expand Sales Coverage
Sales Territory Management: A Definitive Guide to Expand Sales Coverage
 
Salesforce AI Associate Certification.pptx
Salesforce AI Associate Certification.pptxSalesforce AI Associate Certification.pptx
Salesforce AI Associate Certification.pptx
 
OpenChain Webinar: Universal CVSS Calculator
OpenChain Webinar: Universal CVSS CalculatorOpenChain Webinar: Universal CVSS Calculator
OpenChain Webinar: Universal CVSS Calculator
 
Streamlining Your Application Builds with Cloud Native Buildpacks
Streamlining Your Application Builds  with Cloud Native BuildpacksStreamlining Your Application Builds  with Cloud Native Buildpacks
Streamlining Your Application Builds with Cloud Native Buildpacks
 
Big Data Bellevue Meetup | Enhancing Python Data Loading in the Cloud for AI/ML
Big Data Bellevue Meetup | Enhancing Python Data Loading in the Cloud for AI/MLBig Data Bellevue Meetup | Enhancing Python Data Loading in the Cloud for AI/ML
Big Data Bellevue Meetup | Enhancing Python Data Loading in the Cloud for AI/ML
 
Cybersecurity Challenges with Generative AI - for Good and Bad
Cybersecurity Challenges with Generative AI - for Good and BadCybersecurity Challenges with Generative AI - for Good and Bad
Cybersecurity Challenges with Generative AI - for Good and Bad
 
Top Software Development Trends in 2024
Top Software Development Trends in  2024Top Software Development Trends in  2024
Top Software Development Trends in 2024
 
JS-Experts - Cybersecurity for Generative AI
JS-Experts - Cybersecurity for Generative AIJS-Experts - Cybersecurity for Generative AI
JS-Experts - Cybersecurity for Generative AI
 
ARM Talk @ Rejekts - Will ARM be the new Mainstream in our Data Centers_.pdf
ARM Talk @ Rejekts - Will ARM be the new Mainstream in our Data Centers_.pdfARM Talk @ Rejekts - Will ARM be the new Mainstream in our Data Centers_.pdf
ARM Talk @ Rejekts - Will ARM be the new Mainstream in our Data Centers_.pdf
 
Webinar_050417_LeClair12345666777889.ppt
Webinar_050417_LeClair12345666777889.pptWebinar_050417_LeClair12345666777889.ppt
Webinar_050417_LeClair12345666777889.ppt
 
Your Vision, Our Expertise: TECUNIQUE's Tailored Software Teams
Your Vision, Our Expertise: TECUNIQUE's Tailored Software TeamsYour Vision, Our Expertise: TECUNIQUE's Tailored Software Teams
Your Vision, Our Expertise: TECUNIQUE's Tailored Software Teams
 

Beyond Scala Lens

  • 1. Beyond Scala Lenses github: @julien-truffaut or twitter: @JulienTruffaut
  • 2. Function S A A function transforms all s in S into an A
  • 4. Isomorphism S A For all s: S, g(f(s)) == s For all a: A, f(g(a)) == a f g
  • 5. Iso case class Iso[S,A]( get : S => A, reverseGet: A => S ) Properties: For all s: S, reverseGet(get(s)) == s For all a: A, get(reverseGet(a)) == a
  • 9. Iso Derived Methods case class Iso[S,A]( get : S => A, reverseGet: A => S ){ def modify(m: A => A): S => S def reverse: Iso[A,S] def compose[B](other: Iso[A,B]): Iso[S,B] }
  • 10. Units class Robot{ def moveBy(d: Double): Robot } val nono: Robot = … nono.moveBy(100.5) // Meters nono.moveBy(3) // Kilometers nono.moveBy(-2.5) // Yards
  • 11. Safe Unit case class Meter(d: Double) case class Yard(d: Double) class Robot{ def moveBy(m: Meter): Robot } nono.moveBy(Meter(100.5)) nono.moveBy(10) // does not compile nono.moveBy(Yard(3.0)) // does not compile
  • 13. Meter to Yard val meterToYard: Iso[Meter, Yard] = Iso( m => Yard(m.value * 1.09361), y => Meter(y.value / 1.09361) ) meterToYard.get(Meter(200)) == Yard(218.7219999…) nono.moveBy(meterToYard.reverseGet(Yard(2.5))
  • 16. Iso Composition case class Kilometer(value: Double) case class Mile(value: Double) val meterToKilometer: Iso[Meter, Kilometer] = … val yardToMile : Iso[Yard, Mile] = … val kilometerToMile: Iso[Kilometer, Mile] = meterToKilometer.reverse compose meterToYard compose yardToMile
  • 17. Different Representation val stringToList: Iso[String, List[Char]] = Iso(_.toList, _.mkString(“”)) def listToVector[A]: Iso[List[A], Vector[A]] = Iso(_.toVector, _.toList)
  • 18. Generic Programming case object Red val red: Iso[Red, Unit] = Iso(Red => (), () => Red) case class Person(name: String, age: Int) val personToTuple: Iso[Person, (String, Int)] = …
  • 19. Iso Properties For all s: S, reverseGet(get(s)) == s For all a: A, get(reverseGet(a)) == a
  • 20. Scalacheck def isoLaws[S,A](iso: Iso[S,A]) = new Properties { property(“One Way”) = forAll { s: S => iso.reverseGet(iso.get(s)) == s } property(“Other Way”) = forAll { a: A => iso.get(iso.reverseGet(a)) == a } }
  • 21. Scalacheck import org.spec2.scalaz.Spec class IsoSpec extends Spec { checkAll(“meter to yard”, isoLaws(meterToYard)) } val meterToYard: Iso[Meter, Yard] = Iso( m => Yard(m.value * 1.09361), y => Meter(y.value / 1.09361) )
  • 22. Scalacheck A counter-example is: [Yard(1.518707721E9)] (after 24 tries) scala> meterToYard.get(meterToYard.reverseGet( Yard(1.518707721E9))) scala> res0: Yard= Yard(1.5187077210000002E9)
  • 23. Relaxed Isomorphism S A For all s: S such as f(s) exists, g(f(s)) == s For all a: A, f(g(a)) == a ? f g f is a Function[S, Option[A]] g is a Function[A, S]
  • 24. Prism case class Prism[S,A]( getOption : S => Option[A], reverseGet: A => S ) Properties: For all s: S, getOption(s) map reverseGet == Some(s) || None For all a: A, getOption(reverseGet(a)) == Some(a)
  • 25. Pattern matching / Constructor sealed trait List[A] case class Cons[A](h: A, t: List[A]) extends List[A] case class Nil[A]() extends List[A] Cons.unapply(List(1,2,3)) == Some((1, List(2,3))) Cons.unapply(Nil) == None Cons.apply(1, List(2,3)) == List(1,2,3)
  • 26. Prism sealed trait List[A] case class Cons[A](h: A, t: List[A]) extends List[A] case class Nil[A]() extends List[A] def cons[A]: Prism[List[A], (A, List[A])] = … cons.getOption(List(1,2,3)) == Some((1, List(2,3))) cons.getOption(Nil) == None cons.reverseGet(1, List(2,3)) == List(1,2,3)
  • 27. Prism Derived Methods case class Prism[S,A]( getOption: S => Option[A], reverseGet: A => S ){ def isMatching(s: S): Boolean def modify(f: A => A): S=> S def modifyOption(f: A => A): S => Option[S] def compose[B](other: Prism[A,B]): Prism[S,B] def compose[B](other: Iso[A,B]): ???[S,B] }
  • 28. Iso – Prism Optic f g Iso S => A A => S Prism S => Option[A] A => S def isoToPrism[S,A](iso: Iso[S,A]): Prism[S,A] = Prism( getOption = s => Some(iso.get(s)), reverseGet = iso.reverseGet )
  • 29. Iso – Prism case class Prism[S,A]{ def compose[B](other: Prism[A,B]): Prism[S,B] def compose[B](other: Iso[A,B]): Prism[S,B] } case class Iso[S,A]{ def compose[B](other: Iso[A,B]): Iso[S,B] def compose[B](other: Prism[A,B]): Prism[S,B] }
  • 30. Enum sealed trait Day case object Monday extends Day case object Tuesday extends Day val tuesday: Prism[Day, Unit] = … tuesday.getOption(Monday) == None tuesday.getOption(Tuesday) == Some(()) tuesday.reverseGet(()) == Tuesday
  • 31. Json sealed trait Json case class JNumber(v: Double) extends Json case class JString(s: String) extends Json val jNum: Prism[Json, Double] = … jNum.modify(_ + 1)(JNumber(2.0)) == JNumber(3.0) jNum.modify(_ + 1)(JString(“”)) == JString(“”) jNum.modifyOption(_ + 1)(JString(“”)) == None
  • 32. Safe Down Casting val doubleToInt: Prism[Double, Int] = … doubleToInt.getOption(3.4) == None doubleToInt.getOption(3.0) == Some(3) doubleToInt.reverseGet(5) == 5.0
  • 33. Prism Composition sealed trait Json case class JNumber(v: Double) extends Json case class JString(s: String) extends Json val jInt: Prism[Json, Int] = jNum compose doubleToInt jInt.getOption(JNumber(3.0)) == Some(3) jInt.getOption(JNumber(5.9)) == None jInt.getOption(JString(“”)) == None
  • 34. Where is the bug? def stringToInt: Prism[String, Int] = Prism( getOption = s => Try(s.toInt).toOption, reverseGet = _.toString ) stringToInt.modify(_ * 2)(“5”) == “10” stringToInt.getOption(“5”) == Some(5) stringToInt.getOption(“-3”) == Some(-3) stringToInt.getOption(“5.7”) == None stringToInt.getOption(“99999999999999999”) == None stringToInt.getOption(“Hello”) == None
  • 36. Prism S A B COr Or sealed trait S class A extends S class B extends S class C extends S
  • 37. Lens S A B CAnd And case class S(a: A, b: B, c: C)
  • 38. Lens case class Lens[S,A]( get: S => A, set:(A, S) => S ) Properties: For all s: S, set(get(s), s) == s For all a: A, s: S, get(set(a, s)) == a
  • 39. Iso – Lens Optic f g Iso S => A A => S Lens S => A (A, S) => S def isoToLens[S,A](iso: Iso[S,A]): Lens[S,A] = Lens( get = iso.get, set = (a, _) => iso.reverseGet(a) )
  • 40. Accessors case class Person(name: String, age: Int) val age = Lens[Person, Int](_.age, (a, p) => p.copy(age = a)) val zoe = Person(“Zoe”, 25) age.get(zoe) == 25 age.set(20, zoe) == Person(“Zoe”, 20) age.modify(_ + 1)(zoe) == Person(“Zoe”, 26)
  • 41. Nested Accessors case class Person(name: String, age: Int, address: Address) case class Address(streetNumber: Int, StreetName: String) val address: Lens[Person, Address] = … val streetName: Lens[Address, String] = … val zoe = Person(“Zoe”, 25, Address(10, “High Street”)) (address compose streetName).get(zoe) == “High Street” (address compose streetName).set(“Iffley Road”, zoe) == Person(“Zoe”, 25, Address(10, “Iffley Road”))
  • 42. Tuples def first[A,B]: Lens[(A,B), A] = … def second[A,B]: Lens[(A,B), B] = … val tuple = (2,“Zoe”) first.set(4, tuple) == (4,“Zoe”) second.modify(_.reverse)(tuple) == (2,“eoZ”)
  • 43. Universal Case Class Accessors case class Person(name: String, age: Int) val personToTuple: Iso[Person,(String,Int)] = … val zoe = Person(“Zoe”, 25) (personToTuple compose second).set(30, zoe) == Person(“Zoe”, 30)
  • 44. At def at[K,V](k: K): Lens[Map[K,V], Option[V]] = Lens( get = m => m.get(k), set = (optV, m) => optV match { case None => m – k case Some(v)=> m + (k -> v) } )
  • 46. What Next? Optic f g Iso S => A A => S Prism S => Option[A] A => S Lens S => A (A, S) => S
  • 47. Optional Optic f g Iso S => A A => S Prism S => Option[A] A => S Lens S => A (A, S) => S Optional S => Option[A] (A, S) => S
  • 49. Optional case class Optional[S,A]( getOption: S => Option[A], set :(A, S) => S ) Properties: For all s: S, getOption(s) map set(_, s) == Some(s) For all a: A, s: S, getOption(set(a, s)) == Some(a) || None
  • 50. Head def cons[A]: Prism[List[A], (A, List[A])] = … def first[A, B]: Lens[(A, B), A] = … def head[A]: Optional[List[A], A] = cons compose first head.getOption(List(1,2,3)) = Some(1) head.set(0, List(1,2,3)) = List(0,2,3)
  • 51. Void def void[S,A]: Optional[S, A] = Optional( getOption = s => None, set = (a, s) => s ) void.getOption(“Hello”) = None void.set(1,“Hello”) = “Hello” void.setOption(1,“Hello”) = None
  • 52. Index def index[A](i: Int): Optional[List[A], A] = if(i < 0) void else if(i == 0) head else cons compose second compose index(i – 1) index(-1).getOption(List(1,2,3)) == None index(1).getOption(List(1,2,3)) == Some(2) index(5).getOption(List(1,2,3)) == None index(1).set(10, List(1,2,3)) == List(1,10,3)
  • 53. Index for Vector def vectorToList[A]: Iso[Vector[A], List[A]] = … def indexV(i: Int): Optional[Vector[A], A] = vectorToList compose index(i)
  • 54. Index != At 3 5 6 2 0 1 2 3 3 5 99 2 0 1 2 3 index(2).set(99, l) val l = List(3,5,6,2) 3 5 6 2 ? ? ? 99 0 1 2 3 4 5 6 7 3 5 6 2 0 1 2 3 index(7).set(99, l)
  • 55. Study Case: Http Request sealed trait Method case object GET extends Method case object POST extends Method case class URI( host: String, port: Int, path: String, query: Map[String, String] ) case class Request( method: Method, uri: URI, headers: Map[String, String], body: String )
  • 56. Study Case: Http Request val r = Request( method = GET, uri = URI(“localhost”,8080,“/ping”,Map(“age”->“15”)), headers = Map.empty, body = “” )
  • 57. Which method? val method: Lens[Request, Method] = … val GET: Prism[Method, Unit] = … method.get(r) // GET (method compose GET).isMatching(r) // true
  • 58. How to update host? val uri: Lens[Request, URI] = … val host: Lens[URI, String] = … (uri compose host).set(“foo.io”)(r) Request( method = GET, uri = URI(“foo.io”,8080,“/ping”,Map(“age”->“15”)), headers = Map.empty, body = “” )
  • 59. How to increase age query? val query: Lens[URI, Map[String, String]] = … (uri compose query compose index(“age”) compose stringToInt ).modify(_ + 1)(r) Request( method = GET, uri = URI(“localhost”,8080,“/ping”,Map(“age”->“16”)), headers = Map.empty, body = “” )
  • 60. How to add header? val headers: Lens[Request, Map[String, String]] = … (headers compose at(“Content-Length”)).set(Some(“0”))(r) Request( method = GET, uri = URI(“localhost”,8080,“/ping”,Map(“age”->“15”)), headers = Map(“Content-Length”->“0”), body = “” )
  • 61. More power!!! val r2 = Request( method = POST, uri = URI(“localhost”,8080,“/pong”,Map.empty), headers = Map(“x-custom1”->“5”, “x-custom2”->“10”), body = “Hello World” )
  • 62. Traversal (headers compose filterIndex(_.startsWith(“x-”)) compose stringToInt ).modify(_ * 2)(r2) Request( method = POST, uri = URI(“localhost”,8080,“/pong”,Map.empty), headers = Map(“x-custom1”->“10”, “x-custom2”->“20”), body = “Hello World” )
  • 64. Monocle goodies ▪ Provides lots of built-in optics and functions ▪ Macros for creating Lenses, Iso and Prism ▪ Syntax to use optics as infix operator ▪ Experimental state support
  • 65. Resources ▪ Monocle on github ▪ Simon Peyton Jones’s lens talk at Scala Exchange 2013 ▪ Edward Kmett on Lenses with the State Monad
  • 66. Acknowledgement ▪ Member Monocle and Cats gitter channel for advice and review ▪ Special thanks to Ilan Godik (@NightRa) for helping with slides and content