SlideShare una empresa de Scribd logo
1 de 67
Descargar para leer sin conexión
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!

Más contenido relacionado

La actualidad más candente

Array in c language
Array in c languageArray in c language
Array in c language
home
 

La actualidad más candente (20)

Applicative style programming
Applicative style programmingApplicative style programming
Applicative style programming
 
Implementing the IO Monad in Scala
Implementing the IO Monad in ScalaImplementing the IO Monad in Scala
Implementing the IO Monad in Scala
 
Exceptions in python
Exceptions in pythonExceptions in python
Exceptions in python
 
Category Theory in 10 Minutes
Category Theory in 10 MinutesCategory Theory in 10 Minutes
Category Theory in 10 Minutes
 
Chapter 07 inheritance
Chapter 07 inheritanceChapter 07 inheritance
Chapter 07 inheritance
 
Introduction to Recursion (Python)
Introduction to Recursion (Python)Introduction to Recursion (Python)
Introduction to Recursion (Python)
 
Inheritance and polymorphism
Inheritance and polymorphism   Inheritance and polymorphism
Inheritance and polymorphism
 
ZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in Scala
 
Android - Data Storage
Android - Data StorageAndroid - Data Storage
Android - Data Storage
 
Capabilities for Resources and Effects
Capabilities for Resources and EffectsCapabilities for Resources and Effects
Capabilities for Resources and Effects
 
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional ProgrammingZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
 
ZIO Queue
ZIO QueueZIO Queue
ZIO Queue
 
Array in c language
Array in c languageArray in c language
Array in c language
 
PythonOOP
PythonOOPPythonOOP
PythonOOP
 
Application of Stack For Expression Evaluation by Prakash Zodge DSY 41.pptx
Application of Stack For Expression Evaluation by Prakash Zodge DSY 41.pptxApplication of Stack For Expression Evaluation by Prakash Zodge DSY 41.pptx
Application of Stack For Expression Evaluation by Prakash Zodge DSY 41.pptx
 
File Management in C
File Management in CFile Management in C
File Management in C
 
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
 
Object oriented concepts
Object oriented conceptsObject oriented concepts
Object oriented concepts
 
Functors, Applicatives and Monads In Scala
Functors, Applicatives and Monads In ScalaFunctors, Applicatives and Monads In Scala
Functors, Applicatives and Monads In Scala
 
Class, object and inheritance in python
Class, object and inheritance in pythonClass, object and inheritance in python
Class, object and inheritance in python
 

Destacado

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

Destacado (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
 

Similar a Beyond Scala Lens

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

Similar 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

TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
mohitmore19
 
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
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
VictorSzoltysek
 

Último (20)

TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 
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
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
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
 
10 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 202410 Trends Likely to Shape Enterprise Technology in 2024
10 Trends Likely to Shape Enterprise Technology in 2024
 
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
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdfAzure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
Exploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdfExploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdf
 
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected WorkerHow To Troubleshoot Collaboration Apps for the Modern Connected Worker
How To Troubleshoot Collaboration Apps for the Modern Connected Worker
 

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