SlideShare uma empresa Scribd logo
1 de 67
Baixar para ler offline
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!

Mais conteúdo relacionado

Mais procurados

Mais procurados (20)

Functional Error Handling with Cats
Functional Error Handling with CatsFunctional Error Handling with Cats
Functional Error Handling with Cats
 
RedisConf17 - Redis Graph
RedisConf17 - Redis GraphRedisConf17 - Redis Graph
RedisConf17 - Redis Graph
 
Idiomatic Kotlin
Idiomatic KotlinIdiomatic Kotlin
Idiomatic Kotlin
 
API Testing following the Test Pyramid
API Testing following the Test PyramidAPI Testing following the Test Pyramid
API Testing following the Test Pyramid
 
Why functional programming and category theory strongly matters
Why functional programming and category theory strongly mattersWhy functional programming and category theory strongly matters
Why functional programming and category theory strongly matters
 
Monoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and CatsMonoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and Cats
 
Why The Free Monad isn't Free
Why The Free Monad isn't FreeWhy The Free Monad isn't Free
Why The Free Monad isn't Free
 
ZIO-Direct - Functional Scala 2022
ZIO-Direct - Functional Scala 2022ZIO-Direct - Functional Scala 2022
ZIO-Direct - Functional Scala 2022
 
C++20 Key Features Summary
C++20 Key Features SummaryC++20 Key Features Summary
C++20 Key Features Summary
 
Capabilities for Resources and Effects
Capabilities for Resources and EffectsCapabilities for Resources and Effects
Capabilities for Resources and Effects
 
[系列活動] 手把手打開Python資料分析大門
[系列活動] 手把手打開Python資料分析大門[系列活動] 手把手打開Python資料分析大門
[系列活動] 手把手打開Python資料分析大門
 
Celery의 빛과 그림자
Celery의 빛과 그림자Celery의 빛과 그림자
Celery의 빛과 그림자
 
KSUG 스프링캠프 2019 발표자료 - "무엇을 테스트할 것인가, 어떻게 테스트할 것인가"
KSUG 스프링캠프 2019 발표자료 - "무엇을 테스트할 것인가, 어떻게 테스트할 것인가"KSUG 스프링캠프 2019 발표자료 - "무엇을 테스트할 것인가, 어떻게 테스트할 것인가"
KSUG 스프링캠프 2019 발표자료 - "무엇을 테스트할 것인가, 어떻게 테스트할 것인가"
 
ZIO Queue
ZIO QueueZIO Queue
ZIO Queue
 
Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Map(), flatmap() and reduce() are your new best friends: simpler collections,...Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Map(), flatmap() and reduce() are your new best friends: simpler collections,...
 
Testing Spring Boot application in post-JUnit 4 world
Testing Spring Boot application in post-JUnit 4 worldTesting Spring Boot application in post-JUnit 4 world
Testing Spring Boot application in post-JUnit 4 world
 
One Monad to Rule Them All
One Monad to Rule Them AllOne Monad to Rule Them All
One Monad to Rule Them All
 
The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...
 
Redux Sagas - React Alicante
Redux Sagas - React AlicanteRedux Sagas - React Alicante
Redux Sagas - React Alicante
 
UniRx - Reactive Extensions for Unity(EN)
UniRx - Reactive Extensions for Unity(EN)UniRx - Reactive Extensions for Unity(EN)
UniRx - Reactive Extensions for Unity(EN)
 

Destaque

Ekmett勉強会発表資料
Ekmett勉強会発表資料Ekmett勉強会発表資料
Ekmett勉強会発表資料
時響 逢坂
 

Destaque (20)

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
 
今から始める Lens/Prism
今から始める Lens/Prism今から始める Lens/Prism
今から始める Lens/Prism
 
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
 

Semelhante a Beyond Scala Lens

関数潮流(Function Tendency)
関数潮流(Function Tendency)関数潮流(Function Tendency)
関数潮流(Function Tendency)
riue
 

Semelhante a 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
 

Último

CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
9953056974 Low Rate Call Girls In Saket, Delhi NCR
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
shinachiaurasa2
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
masabamasaba
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
Health
 
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfintroduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
VishalKumarJha10
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
masabamasaba
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
masabamasaba
 

Último (20)

Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK Software
 
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
 
Right Money Management App For Your Financial Goals
Right Money Management App For Your Financial GoalsRight Money Management App For Your Financial Goals
Right Money Management App For Your Financial Goals
 
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...
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton 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
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfintroduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
 
%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
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
 
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 

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