SlideShare a Scribd company logo
"Getting acquainted with Lens"
@EncodePanda
@EncodePanda, paul.szulc@gmail.com 1
This talk is about Lens1
1. What problem they are trying to solve?
2. How we can use them?
3. How they are being implemented?
1 
https://hackage.haskell.org/package/lens.
@EncodePanda, paul.szulc@gmail.com 2
There are two things fundamentally
broken in Haskell
1. Record syntax
2. Strings
@EncodePanda, paul.szulc@gmail.com 3
There are two things fundamentally
broken in Haskell
1. Record syntax
2. Strings
@EncodePanda, paul.szulc@gmail.com 4
Record syntax broken?
"The record system is a continual source of pain"
- Stephen Diehl
@EncodePanda, paul.szulc@gmail.com 5
Record syntax broken?
"What is your least favorite thing about Haskell?
Records are still tedious"
- 2018 State of Haskell Survey
@EncodePanda, paul.szulc@gmail.com 6
Record syntax broken?
"Shitty records."
- Someone on reddit
@EncodePanda, paul.szulc@gmail.com 7
The Example
@EncodePanda, paul.szulc@gmail.com 8
data Conference = Conference
{ organizer :: Organizer
, speakers :: [Speaker]
} deriving Show
@EncodePanda, paul.szulc@gmail.com 9
data Conference = Conference
{ organizer :: Organizer
, speakers :: [Speaker]
} deriving Show
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
@EncodePanda, paul.szulc@gmail.com 10
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
@EncodePanda, paul.szulc@gmail.com 11
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
data Name = Name
{ firstName :: String
, lastName :: String
} deriving Show
@EncodePanda, paul.szulc@gmail.com 12
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
data Contact = Contact
{ address :: Address
, email :: String
} deriving Show
@EncodePanda, paul.szulc@gmail.com 13
data Address = Address
{ street :: String
, city :: String
, country :: String
} deriving Show
@EncodePanda, paul.szulc@gmail.com 14
data Conference = Conference
{ organizer :: Organizer
, speakers :: [Speaker]
} deriving Show
@EncodePanda, paul.szulc@gmail.com 15
data Conference = Conference
{ organizer :: Organizer
, speakers :: [Speaker]
} deriving Show
data Speaker = Speaker
{ slidesReady :: Bool
} deriving Show
@EncodePanda, paul.szulc@gmail.com 16
Getting things from a
record
@EncodePanda, paul.szulc@gmail.com 17
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
oli :: Organizer
oli = Organizer
{ name = Name "Oli" "Makhasoeva"
, contact = classified
}
@EncodePanda, paul.szulc@gmail.com 18
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
oli :: Organizer
oli = Organizer
{ name = Name "Oli" "Makhasoeva"
, contact = classified
}
@EncodePanda, paul.szulc@gmail.com 19
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
λ> :t name
name :: Organizer -> Name
@EncodePanda, paul.szulc@gmail.com 20
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
λ> name oli
Name {firstName = "Oli", lastName = "Makhasoeva"}
@EncodePanda, paul.szulc@gmail.com 21
import Data.Function ((&))
λ> :t (&)
(&) :: a -> (a -> b) -> b
λ> length [4, 6, 8]
3
λ> [4, 6, 8] & length
3
@EncodePanda, paul.szulc@gmail.com 22
import Data.Function ((&))
λ> :t (&)
(&) :: a -> (a -> b) -> b
λ> length [4, 6, 8]
3
λ> [4, 6, 8] & length
3
@EncodePanda, paul.szulc@gmail.com 23
import Data.Function ((&))
λ> :t (&)
(&) :: a -> (a -> b) -> b
λ> length [4, 6, 8]
3
λ> [4, 6, 8] & length
3
@EncodePanda, paul.szulc@gmail.com 24
import Data.Function ((&))
λ> :t (&)
(&) :: a -> (a -> b) -> b
λ> length [4, 6, 8]
3
λ> [4, 6, 8] & length
3
@EncodePanda, paul.szulc@gmail.com 25
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
λ> name oli
Name {firstName = "Oli", lastName = "Makhasoeva"}
@EncodePanda, paul.szulc@gmail.com 26
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
λ> oli & name
Name {firstName = "Oli", lastName = "Makhasoeva"}
@EncodePanda, paul.szulc@gmail.com 27
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
λ> oli & name & firstName
"Oli"
@EncodePanda, paul.szulc@gmail.com 28
organizerCountry :: Conference -> String
organizerCountry conf =
conf & organizer
& contact
& address
& country
@EncodePanda, paul.szulc@gmail.com 29
But
@EncodePanda, paul.szulc@gmail.com 30
data Conference = Conference
{ organizer :: Organizer
, speakers :: [Speaker]
} deriving Show
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
data Speaker = Speaker
{ slidesReady :: Bool
} deriving Show
@EncodePanda, paul.szulc@gmail.com 31
data Conference = Conference
{ name :: String
, organizer :: Organizer
, speakers :: [Speaker]
} deriving Show
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
data Speaker = Speaker
{ name :: Name
, slidesReady :: Bool
} deriving Show
@EncodePanda, paul.szulc@gmail.com 32
data Conference = Conference
{ name :: String
, organizer :: Organizer
, speakers :: [Speaker]
} deriving Show
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
data Speaker = Speaker
{ name :: Name
, slidesReady :: Bool
} deriving Show
@EncodePanda, paul.szulc@gmail.com 33
/../src/WhyLens.hs:27:5: error:
Multiple declarations of ‘name’
Declared at: src/WhyLens.hs:12:5
src/WhyLens.hs:27:5
|
27 | { name :: Name
|
@EncodePanda, paul.szulc@gmail.com 34
We can actually do
something about it
@EncodePanda, paul.szulc@gmail.com 35
@EncodePanda, paul.szulc@gmail.com 36
{-# LANGUAGE DuplicateRecordFields #-}
@EncodePanda, paul.szulc@gmail.com 37
{-# LANGUAGE DuplicateRecordFields #-}
module WhyLens where
data Conference = Conference
{ name :: String
, organizer :: Organizer
, speakers :: [Speaker]
} deriving Show
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
data Speaker = Speaker
{ name :: Name
, slidesReady :: Bool
} deriving Show
@EncodePanda, paul.szulc@gmail.com 38
{-# LANGUAGE DuplicateRecordFields #-}
module WhyLens where
data Conference = Conference
{ name :: String
, organizer :: Organizer
, speakers :: [Speaker]
} deriving Show
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
data Speaker = Speaker
{ name :: Name
, slidesReady :: Bool
} deriving Show
@EncodePanda, paul.szulc@gmail.com 39
But
@EncodePanda, paul.szulc@gmail.com 40
organizerName :: Conference -> Name
organizerName conference =
conference & organizer & name
@EncodePanda, paul.szulc@gmail.com 41
/../src/WhyLens.hs:69:28: error:
Ambiguous occurrence ‘name’
It could refer to
either the field ‘name’, defined at src/WhyLens.hs:34:5
or the field ‘name’, defined at src/WhyLens.hs:19:5
or the field ‘name’, defined at src/WhyLens.hs:13:5
@EncodePanda, paul.szulc@gmail.com 42
@EncodePanda, paul.szulc@gmail.com 43
{-# LANGUAGE OverloadedLabels #-}
@EncodePanda, paul.szulc@gmail.com 44
@EncodePanda, paul.szulc@gmail.com 45
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedLabels #-}
@EncodePanda, paul.szulc@gmail.com 46
class IsLabel (x :: Symbol) a where
fromLabel :: a
instance IsLabel "encodepanda" Speaker where
fromLabel = Speaker
{ name = Name "Pawel" "Szulc"
, slidesReady = False
}
pawel :: Speaker
pawel = fromLabel @"encodepanda"
@EncodePanda, paul.szulc@gmail.com 47
class IsLabel (x :: Symbol) a where
fromLabel :: a
instance IsLabel "encodepanda" Speaker where
fromLabel = Speaker
{ name = Name "Pawel" "Szulc"
, slidesReady = False
}
pawel :: Speaker
pawel = fromLabel @"encodepanda"
@EncodePanda, paul.szulc@gmail.com 48
class IsLabel (x :: Symbol) a where
fromLabel :: a
instance IsLabel "encodepanda" Speaker where
fromLabel = Speaker
{ name = Name "Pawel" "Szulc"
, slidesReady = False
}
pawel :: Speaker
pawel = fromLabel @"encodepanda"
@EncodePanda, paul.szulc@gmail.com 49
class IsLabel (x :: Symbol) a where
fromLabel :: a
instance IsLabel "encodepanda" Speaker where
fromLabel = Speaker
{ name = Name "Pawel" "Szulc"
, slidesReady = False
}
pawel :: Speaker
pawel = fromLabel @"encodepanda"
@EncodePanda, paul.szulc@gmail.com 50
class IsLabel (x :: Symbol) a where
fromLabel :: a
instance IsLabel "encodepanda" Speaker where
fromLabel = Speaker
{ name = Name "Pawel" "Szulc"
, slidesReady = False
}
pawel :: Speaker
pawel = #encodepanda
@EncodePanda, paul.szulc@gmail.com 51
organizerName :: Conference -> Name
organizerName conference =
conference & organizer & name
@EncodePanda, paul.szulc@gmail.com 52
/../src/WhyLens.hs:69:28: error:
Ambiguous occurrence ‘name’
It could refer to
either the field ‘name’, defined at src/WhyLens.hs:34:5
or the field ‘name’, defined at src/WhyLens.hs:19:5
or the field ‘name’, defined at src/WhyLens.hs:13:5
@EncodePanda, paul.szulc@gmail.com 53
data Conference = Conference
{ name :: String
, organizer :: Organizer
, speakers :: [Speaker]
} deriving Show
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
data Speaker = Speaker
{ name :: Name
, slidesReady :: Bool
} deriving Show
@EncodePanda, paul.szulc@gmail.com 54
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
instance IsLabel "name" (Organizer -> Name) where
fromLabel = name
@EncodePanda, paul.szulc@gmail.com 55
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
instance IsLabel "name" (Organizer -> Name) where
fromLabel = name
@EncodePanda, paul.szulc@gmail.com 56
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
instance IsLabel "name" (Organizer -> Name) where
fromLabel = name
organizerName :: Conference -> Name
organizerName conference =
conference & organizer & name
@EncodePanda, paul.szulc@gmail.com 57
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
instance IsLabel "name" (Organizer -> Name) where
fromLabel = name
organizerName :: Conference -> Name
organizerName conference =
conference & organizer & #name
@EncodePanda, paul.szulc@gmail.com 58
@EncodePanda, paul.szulc@gmail.com 59
@EncodePanda, paul.szulc@gmail.com 60
Setting things to a record
@EncodePanda, paul.szulc@gmail.com 61
λ> haskellLove
Conference
{ name = "Haskell.Love"
, organizer = Organizer {..}
, speakers = []
}
@EncodePanda, paul.szulc@gmail.com 62
λ> haskellLove
Conference
{ name = "Haskell.Love"
, organizer = Organizer {..}
, speakers = []
}
@EncodePanda, paul.szulc@gmail.com 63
λ> haskellLove { speakers = [ pawel, marcin ] }
Conference
{ name = "Haskell.Love"
, organizer = Organizer {..}
, speakers = [ Speaker { name = Name { firstName = "Pawel"
, lastName = "Szulc"}
, slidesReady = False
}
, Speaker { name = Name { firstName = "Marcin"
, lastName = "Rzeznicki"}
, slidesReady = False}
]
}
@EncodePanda, paul.szulc@gmail.com 64
λ> haskellLove { speakers = [ pawel, marcin ] }
Conference
{ name = "Haskell.Love"
, organizer = Organizer {..}
, speakers = [ Speaker { name = Name { firstName = "Pawel"
, lastName = "Szulc"}
, slidesReady = False
}
, Speaker { name = Name { firstName = "Marcin"
, lastName = "Rzeznicki"}
, slidesReady = False}
]
}
@EncodePanda, paul.szulc@gmail.com 65
But
@EncodePanda, paul.szulc@gmail.com 66
allSpeakersNotReady :: Conference -> Conference
allSpeakersNotReady conference =
let
oldSpeakers = conference & speakers
in
conference {
speakers =
fmap (s -> s { slidesReady = False}) oldSpeakers
}
@EncodePanda, paul.szulc@gmail.com 67
allSpeakersNotReady :: Conference -> Conference
allSpeakersNotReady conference =
let
oldSpeakers = conference & speakers
in
conference {
speakers =
fmap (s -> s { slidesReady = False}) oldSpeakers
}
@EncodePanda, paul.szulc@gmail.com 68
allSpeakersNotReady :: Conference -> Conference
allSpeakersNotReady conference =
let
oldSpeakers = conference & speakers
in
conference {
speakers =
fmap (s -> s { slidesReady = False}) oldSpeakers
}
@EncodePanda, paul.szulc@gmail.com 69
changeOrganizerEmail :: (String -> String) -> Conference -> Conference
changeOrganizerEmail modifyEmail conference =
let
oldOrganizer = conference & organizer
newContact = (oldOrganizer & contact)
{ email = modifyEmail (oldOrganizer & contact & email)
}
newOrganizer = oldOrganizer { contact = newContact}
in
conference { organizer = newOrganizer }
@EncodePanda, paul.szulc@gmail.com 70
changeOrganizerEmail :: (String -> String) -> Conference -> Conference
changeOrganizerEmail modifyEmail conference =
let
oldOrganizer = conference & organizer
newContact = (oldOrganizer & contact)
{ email = modifyEmail (oldOrganizer & contact & email)
}
newOrganizer = oldOrganizer { contact = newContact}
in
conference { organizer = newOrganizer }
@EncodePanda, paul.szulc@gmail.com 71
changeOrganizerEmail :: (String -> String) -> Conference -> Conference
changeOrganizerEmail modifyEmail conference =
let
oldOrganizer = conference & organizer
newContact = (oldOrganizer & contact)
{ email = modifyEmail (oldOrganizer & contact & email)
}
newOrganizer = oldOrganizer { contact = newContact}
in
conference { organizer = newOrganizer }
@EncodePanda, paul.szulc@gmail.com 72
changeOrganizerEmail :: (String -> String) -> Conference -> Conference
changeOrganizerEmail modifyEmail conference =
let
oldOrganizer = conference & organizer
newContact = (oldOrganizer & contact)
{ email = modifyEmail (oldOrganizer & contact & email)
}
newOrganizer = oldOrganizer { contact = newContact}
in
conference { organizer = newOrganizer }
@EncodePanda, paul.szulc@gmail.com 73
changeOrganizerEmail :: (String -> String) -> Conference -> Conference
changeOrganizerEmail modifyEmail conference =
let
oldOrganizer = conference & organizer
newContact = (oldOrganizer & contact)
{ email = modifyEmail (oldOrganizer & contact & email)
}
newOrganizer = oldOrganizer { contact = newContact}
in
conference { organizer = newOrganizer }
@EncodePanda, paul.szulc@gmail.com 74
changeOrganizerEmail :: (String -> String) -> Conference -> Conference
changeOrganizerEmail modifyEmail conference =
let
oldOrganizer = conference & organizer
newContact = (oldOrganizer & contact)
{ email = modifyEmail (oldOrganizer & contact & email)
}
newOrganizer = oldOrganizer { contact = newContact}
in
conference { organizer = newOrganizer }
@EncodePanda, paul.szulc@gmail.com 75
@EncodePanda, paul.szulc@gmail.com 76
What can you do about it
1. Change the programming language
2. Quit
3. Ask the Oracle
@EncodePanda, paul.szulc@gmail.com 77
@EncodePanda, paul.szulc@gmail.com 78
@EncodePanda, paul.szulc@gmail.com 79
Lens1
1 
https://hackage.haskell.org/package/lens.
@EncodePanda, paul.szulc@gmail.com 80
@EncodePanda, paul.szulc@gmail.com 81
@EncodePanda, paul.szulc@gmail.com 82
@EncodePanda, paul.szulc@gmail.com 83
@EncodePanda, paul.szulc@gmail.com 84
@EncodePanda, paul.szulc@gmail.com 85
@EncodePanda, paul.szulc@gmail.com 86
@EncodePanda, paul.szulc@gmail.com 87
@EncodePanda, paul.szulc@gmail.com 88
@EncodePanda, paul.szulc@gmail.com 89
@EncodePanda, paul.szulc@gmail.com 90
@EncodePanda, paul.szulc@gmail.com 91
@EncodePanda, paul.szulc@gmail.com 92
@EncodePanda, paul.szulc@gmail.com 93
@EncodePanda, paul.szulc@gmail.com 94
@EncodePanda, paul.szulc@gmail.com 95
@EncodePanda, paul.szulc@gmail.com 96
@EncodePanda, paul.szulc@gmail.com 97
@EncodePanda, paul.szulc@gmail.com 98
@EncodePanda, paul.szulc@gmail.com 99
@EncodePanda, paul.szulc@gmail.com 100
@EncodePanda, paul.szulc@gmail.com 101
@EncodePanda, paul.szulc@gmail.com 102
@EncodePanda, paul.szulc@gmail.com 103
Lens' s a
@EncodePanda, paul.szulc@gmail.com 104
_organizer :: Lens' Conference Organizer
@EncodePanda, paul.szulc@gmail.com 105
_organizer :: Lens' Conference Organizer
λ> view _organizer haskellLove
Organizer
{ name = Name { firstName = "Oli"
, lastName = "Makhasoeva"}
, contact = Contact {..}
}
@EncodePanda, paul.szulc@gmail.com 106
_organizer :: Lens' Conference Organizer
λ> haskellLove ^. _organizer
Organizer
{ name = Name { firstName = "Oli"
, lastName = "Makhasoeva"}
, contact = Contact {..}
}
@EncodePanda, paul.szulc@gmail.com 107
_organizer :: Lens' Conference Organizer
_contact :: Lens' Organizer Contact
_address :: Lens' Contact Address
_country :: Lens' Address String
λ> haskelllove ^. _organizer . _contact . _address . _country
"classified"
@EncodePanda, paul.szulc@gmail.com 108
_organizer :: Lens' Conference Organizer
_contact :: Lens' Organizer Contact
_address :: Lens' Contact Address
_country :: Lens' Address String
λ> haskelllove ^. _organizer . _contact . _address . _country
"classified"
@EncodePanda, paul.szulc@gmail.com 109
_organizer :: Lens' Conference Organizer
_contact :: Lens' Organizer Contact
_address :: Lens' Contact Address
_country :: Lens' Address String
λ> haskelllove ^. _organizer . _contact . _address . _country
"classified"
@EncodePanda, paul.szulc@gmail.com 110
_organizer :: Lens' Conference Organizer
_contact :: Lens' Organizer Contact
_address :: Lens' Contact Address
_country :: Lens' Address String
λ> haskelllove ^. _organizer . _contact . _address . _country
"classified"
@EncodePanda, paul.szulc@gmail.com 111
dot?
@EncodePanda, paul.szulc@gmail.com 112
(.) :: (b -> c) -> (a -> b) -> a -> c
How is that possible?
@EncodePanda, paul.szulc@gmail.com 113
@EncodePanda, paul.szulc@gmail.com 114
_organizer :: Lens' Conference Organizer
_contact :: Lens' Organizer Contact
_address :: Lens' Contact Address
_country :: Lens' Address String
λ> haskelllove ^. _organizer . _contact . _address . _country
"classified"
@EncodePanda, paul.szulc@gmail.com 115
_name :: Lens' Conference String
λ> set _name "Haskell Love 2020" haskellLove
Conference
{ name = "Haskell Love 2020"
, organizer = Organizer {..}
, speakers = [..]
}
@EncodePanda, paul.szulc@gmail.com 116
_name :: Lens' Conference String
λ> (_name .~ "Haskell Love 2020") haskellLove
Conference
{ name = "Haskell Love 2020"
, organizer = Organizer {..}
, speakers = [..]
}
@EncodePanda, paul.szulc@gmail.com 117
_name :: Lens' Conference String
λ> haskellLove & _name .~ "Haskell Love 2020"
Conference
{ name = "Haskell Love 2020"
, organizer = Organizer {..}
, speakers = [..]
}
@EncodePanda, paul.szulc@gmail.com 118
_organizer :: Lens' Conference Organizer
_contact :: Lens' Organizer Contact
_address :: Lens' Contact Address
_country :: Lens' Address String
λ> haskellLove & _organizer . _contact . _address . _country .~ "Poland"
Conference
{ name = "Haskell.Love"
, organizer =
Organizer { name = Name {firstName = "Oli", lastName = "Makhasoeva"}
, contact = Contact { address = Address { street = "Class"
, city = "ified"
, country = "Poland"
}
, email = "oli@haskell.love"}
}
, speakers = [...]
}
@EncodePanda, paul.szulc@gmail.com 119
_organizer :: Lens' Conference Organizer
_contact :: Lens' Organizer Contact
_address :: Lens' Contact Address
_country :: Lens' Address String
λ> haskellLove & _organizer . _contact . _address . _country .~ "Poland"
Conference
{ name = "Haskell.Love"
, organizer =
Organizer { name = Name {firstName = "Oli", lastName = "Makhasoeva"}
, contact = Contact { address = Address { street = "Class"
, city = "ified"
, country = "Poland"
}
, email = "oli@haskell.love"}
}
, speakers = [...]
}
@EncodePanda, paul.szulc@gmail.com 120
_organizer :: Lens' Conference Organizer
_contact :: Lens' Organizer Contact
_address :: Lens' Contact Address
_country :: Lens' Address String
λ> haskellLove & _organizer . _contact . _address . _country .~ "Poland"
Conference
{ name = "Haskell.Love"
, organizer =
Organizer { name = Name {firstName = "Oli", lastName = "Makhasoeva"}
, contact = Contact { address = Address { street = "Class"
, city = "ified"
, country = "Poland"
}
, email = "oli@haskell.love"}
}
, speakers = [...]
}
@EncodePanda, paul.szulc@gmail.com 121
_organizer :: Lens' Conference Organizer
_contact :: Lens' Organizer Contact
_address :: Lens' Contact Address
_country :: Lens' Address String
λ> haskellLove & _organizer . _contact . _address . _country .~ "Poland"
Conference
{ name = "Haskell.Love"
, organizer =
Organizer { name = Name {firstName = "Oli", lastName = "Makhasoeva"}
, contact = Contact { address = Address { street = "Class"
, city = "ified"
, country = "Poland"
}
, email = "oli@haskell.love"}
}
, speakers = [...]
}
@EncodePanda, paul.szulc@gmail.com 122
_organizer :: Lens' Conference Organizer
_contact :: Lens' Organizer Contact
_address :: Lens' Contact Address
_country :: Lens' Address String
λ> haskellLove & _organizer . _contact . _address . _country .~ "Poland"
Conference
{ name = "Haskell.Love"
, organizer =
Organizer { name = Name {firstName = "Oli", lastName = "Makhasoeva"}
, contact = Contact { address = Address { street = "Class"
, city = "ified"
, country = "Poland"
}
, email = "oli@haskell.love"}
}
, speakers = [...]
}
@EncodePanda, paul.szulc@gmail.com 123
_organizer :: Lens' Conference Organizer
_contact :: Lens' Organizer Contact
_address :: Lens' Contact Address
_country :: Lens' Address String
λ> haskellLove & _organizer . _contact . _address . _country .~ "Poland"
& _name .~ "Haskell Love 2021"
Conference
{ name = "Haskell Love 2021"
, organizer =
Organizer { name = Name {firstName = "Oli", lastName = "Makhasoeva"}
, contact = Contact { address = Address { street = "Class"
, city = "ified"
, country = "Poland"
}
, email = "oli@haskell.love"}
}
, speakers = [...]}
@EncodePanda, paul.szulc@gmail.com 124
_organizer :: Lens' Conference Organizer
_contact :: Lens' Organizer Contact
_address :: Lens' Contact Address
_country :: Lens' Address String
λ> haskellLove & _organizer . _contact . _address . _country .~ "Poland"
& _name .~ "Haskell Love 2021"
Conference
{ name = "Haskell Love 2021"
, organizer =
Organizer { name = Name {firstName = "Oli", lastName = "Makhasoeva"}
, contact = Contact { address = Address { street = "Class"
, city = "ified"
, country = "Poland"
}
, email = "oli@haskell.love"}
}
, speakers = [...]}
@EncodePanda, paul.szulc@gmail.com 125
_organizer :: Lens' Conference Organizer
_contact :: Lens' Organizer Contact
_address :: Lens' Contact Address
_country :: Lens' Address String
λ> haskellLove & _organizer . _contact . _address . _country .~ "Poland"
& _name .~ "Haskell Love 2021"
Conference
{ name = "Haskell Love 2021"
, organizer =
Organizer { name = Name {firstName = "Oli", lastName = "Makhasoeva"}
, contact = Contact { address = Address { street = "Class"
, city = "ified"
, country = "Poland"
}
, email = "oli@haskell.love"}
}
, speakers = [...]}
@EncodePanda, paul.szulc@gmail.com 126
_organizer :: Lens' Conference Organizer
_contact :: Lens' Organizer Contact
_address :: Lens' Contact Address
_country :: Lens' Address String
λ> :t lens
lens :: (s -> a) -> (s -> a -> s) -> Lens' s a
@EncodePanda, paul.szulc@gmail.com 127
_organizer :: Lens' Conference Organizer
_contact :: Lens' Organizer Contact
_address :: Lens' Contact Address
_country :: Lens' Address String
λ> :t lens
lens :: (s -> a) -> (s -> a -> s) -> Lens' s a
@EncodePanda, paul.szulc@gmail.com 128
_organizer :: Lens' Conference Organizer
_contact :: Lens' Organizer Contact
_address :: Lens' Contact Address
_country :: Lens' Address String
@EncodePanda, paul.szulc@gmail.com 129
_organizer :: Lens' Conference Organizer
_organizer = lens getter setter
where
getter = organizer
setter c o = c { organizer = o}
@EncodePanda, paul.szulc@gmail.com 130
But
@EncodePanda, paul.szulc@gmail.com 131
_name :: Lens' Conference String
_name = lens getter setter
where
getter = name
setter c n = c { name = n }
/../src/TheLens.hs:23:14: error:
Ambiguous occurrence ‘name’
It could refer to
either the field ‘name’,
imported from ‘Example’ at src/TheLens.hs:6:1-24
or the field ‘name’,
imported from ‘Example’ at src/TheLens.hs:6:1-24
or the field ‘name’,
imported from ‘Example’ at src/TheLens.hs:6:1-24
@EncodePanda, paul.szulc@gmail.com 132
_name :: Lens' Conference String
_name = lens getter setter
where
getter = name
setter c n = c { name = n }
/../src/TheLens.hs:23:14: error:
Ambiguous occurrence ‘name’
It could refer to
either the field ‘name’,
imported from ‘Example’ at src/TheLens.hs:6:1-24
or the field ‘name’,
imported from ‘Example’ at src/TheLens.hs:6:1-24
or the field ‘name’,
imported from ‘Example’ at src/TheLens.hs:6:1-24
@EncodePanda, paul.szulc@gmail.com 133
_name :: Lens' Conference String
_name = lens getter setter
where
getter (Conference n _ _) = n
setter (Conference _ o s) n = Conference n o s
@EncodePanda, paul.szulc@gmail.com 134
_organizer :: Lens' Conference Organizer
_contact :: Lens' Organizer Contact
_address :: Lens' Contact Address
_country :: Lens' Address String
@EncodePanda, paul.szulc@gmail.com 135
_organizer :: Lens' Conference Organizer
_organizer = lens getter setter
where
getter = organizer
setter c o = c { organizer = o}
_name :: Lens' Conference String
_name = lens getter setter
where
getter (Conference n _ _) = n
setter (Conference _ o s) n = Conference n o s
_contact :: Lens' Organizer Contact
_contact = lens contact (o -> c -> o { contact = c})
_address :: Lens' Contact Address
_address = lens address (c -> a -> c { address = a})
_country :: Lens' Address String
_country = lens country (a -> c -> a { country = c})
@EncodePanda, paul.szulc@gmail.com 136
Approach #1 -
TemplateHaskell
@EncodePanda, paul.szulc@gmail.com 137
@EncodePanda, paul.szulc@gmail.com 138
{-# LANGUAGE TemplateHaskell #-}
module TheLens where
data Ticket = Ticket
{ _event :: String
, _price :: Int
} deriving Show
makeLenses ''Ticket
λ> ticket = Ticket "Haskell Love" 0
λ> ticket ^. price
0
λ> ticket & event .~ "Haskell Love 2020"
Ticket {_event = "Haskell Love 2020", _price = 0}
@EncodePanda, paul.szulc@gmail.com 139
{-# LANGUAGE TemplateHaskell #-}
module TheLens where
data Ticket = Ticket
{ _event :: String
, _price :: Int
} deriving Show
makeLenses ''Ticket
λ> ticket = Ticket "Haskell Love" 0
λ> ticket ^. price
0
λ> ticket & event .~ "Haskell Love 2020"
Ticket {_event = "Haskell Love 2020", _price = 0}
@EncodePanda, paul.szulc@gmail.com 140
{-# LANGUAGE TemplateHaskell #-}
module TheLens where
data Ticket = Ticket
{ _event :: String
, _price :: Int
} deriving Show
makeLenses ''Ticket
λ> ticket = Ticket "Haskell Love" 0
λ> ticket ^. price
0
λ> ticket & event .~ "Haskell Love 2020"
Ticket {_event = "Haskell Love 2020", _price = 0}
@EncodePanda, paul.szulc@gmail.com 141
{-# LANGUAGE TemplateHaskell #-}
module TheLens where
data Ticket = Ticket
{ _event :: String
, _price :: Int
} deriving Show
makeLenses ''Ticket
λ> ticket = Ticket "Haskell Love" 0
λ> ticket ^. price
0
λ> ticket & event .~ "Haskell Love 2020"
Ticket {_event = "Haskell Love 2020", _price = 0}
@EncodePanda, paul.szulc@gmail.com 142
{-# LANGUAGE TemplateHaskell #-}
module TheLens where
data Ticket = Ticket
{ _event :: String
, _price :: Int
} deriving Show
makeLenses ''Ticket
λ> ticket = Ticket "Haskell Love" 0
λ> ticket ^. price
0
λ> ticket & event .~ "Haskell Love 2020"
Ticket {_event = "Haskell Love 2020", _price = 0}
@EncodePanda, paul.szulc@gmail.com 143
{-# LANGUAGE TemplateHaskell #-}
module TheLens where
data Ticket = Ticket
{ _event :: String
, _price :: Int
} deriving Show
makeLenses ''Ticket
λ> ticket = Ticket "Haskell Love" 0
λ> ticket ^. price
0
λ> ticket & event .~ "Haskell Love 2020"
Ticket {_event = "Haskell Love 2020", _price = 0}
@EncodePanda, paul.szulc@gmail.com 144
But
@EncodePanda, paul.szulc@gmail.com 145
data Ticket = Ticket
{ _event :: String
, _price :: Int
} deriving Show
makeLenses ''Ticket
data Item = Item
{ _id :: Int
, _price :: Int
}
makeLenses ''Item
/../src/TheLens.hs:39:1: error:
Multiple declarations of ‘price’
Declared at: src/TheLens.hs:33:1
src/TheLens.hs:39:1
|
39 | makeLenses ''Item
| ^^^^^^^^^^^^^^^^^
@EncodePanda, paul.szulc@gmail.com 146
data Ticket = Ticket
{ _event :: String
, _price :: Int
} deriving Show
makeLenses ''Ticket
data Item = Item
{ _id :: Int
, _price :: Int
}
makeLenses ''Item
/../src/TheLens.hs:39:1: error:
Multiple declarations of ‘price’
Declared at: src/TheLens.hs:33:1
src/TheLens.hs:39:1
|
39 | makeLenses ''Item
| ^^^^^^^^^^^^^^^^^
@EncodePanda, paul.szulc@gmail.com 147
data Ticket = Ticket
{ _event :: String
, _price :: Int
} deriving Show
makeLenses ''Ticket
data Item = Item
{ _id :: Int
, _price :: Int
}
makeLenses ''Item
/../src/TheLens.hs:39:1: error:
Multiple declarations of ‘price’
Declared at: src/TheLens.hs:33:1
src/TheLens.hs:39:1
|
39 | makeLenses ''Item
| ^^^^^^^^^^^^^^^^^
@EncodePanda, paul.szulc@gmail.com 148
Approach #2 -
GHC.Generics
@EncodePanda, paul.szulc@gmail.com 149
generic-lens 2
2 
https://hackage.haskell.org/package/generic-lens.
@EncodePanda, paul.szulc@gmail.com 150
data Conference = Conference
{ name :: String
, organizer :: Organizer
, speakers :: [Speaker]
} deriving Show
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving Show
...
@EncodePanda, paul.szulc@gmail.com 151
data Conference = Conference
{ name :: String
, organizer :: Organizer
, speakers :: [Speaker]
} deriving (Generic, Show)
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving (Generic, Show)
...
@EncodePanda, paul.szulc@gmail.com 152
λ> haskellLove ^. field @"organizer" . field @"contact" . field @"email"
"oli@haskell.love"
@EncodePanda, paul.szulc@gmail.com 153
λ> haskellLove ^. #organizer . #contact . #email
"oli@haskell.love"
@EncodePanda, paul.szulc@gmail.com 154
data Conference = Conference
{ name :: String
, organizer :: Organizer
, speakers :: [Speaker]
} deriving (Generic, Show)
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving (Generic, Show)
...
@EncodePanda, paul.szulc@gmail.com 155
data Conference = Conference
{ name :: String
, organizer :: Organizer
, speakers :: [Speaker]
} deriving (Generic, Show)
data Organizer = Organizer
{ name :: Name
, contact :: Contact
} deriving (Generic, Show)
...
@EncodePanda, paul.szulc@gmail.com 156
λ> haskellLove ^. #name
"Haskell.Love"
λ> haskellLove ^. #organizer . #name
Name {firstName = "Oli", lastName = "Makhasoeva"}
@EncodePanda, paul.szulc@gmail.com 157
Lens' s a
@EncodePanda, paul.szulc@gmail.com 158
Lens s t a b
@EncodePanda, paul.szulc@gmail.com 159
"Shitty records."
- Someone on reddit
@EncodePanda, paul.szulc@gmail.com 160
@EncodePanda, paul.szulc@gmail.com 161
Lens s t a b
@EncodePanda, paul.szulc@gmail.com 162
Lens s t a b
λ> :t lens
lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b
type Lens' s a = Lens s s a a
@EncodePanda, paul.szulc@gmail.com 163
Lens s t a b
λ> :t lens
lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b
type Lens' s a = Lens s s a a
@EncodePanda, paul.szulc@gmail.com 164
first :: Lens' (a, b) a
first = lens getter setter
where
getter (a, _) = a
setter (_, b) a = (a, b)
λ> (10, 20) ^. first
10
λ> (10, 20) & first .~ 30
(30,20)
λ> (10, 20) & first .~ "hello"
<interactive>:25:2: error:
• Could not deduce (Num [Char]) arising from the literal ‘10’
from the context: Num b
@EncodePanda, paul.szulc@gmail.com 165
first :: Lens' (a, b) a
first = lens getter setter
where
getter (a, _) = a
setter (_, b) a = (a, b)
λ> (10, 20) ^. first
10
λ> (10, 20) & first .~ 30
(30,20)
λ> (10, 20) & first .~ "hello"
<interactive>:25:2: error:
• Could not deduce (Num [Char]) arising from the literal ‘10’
from the context: Num b
@EncodePanda, paul.szulc@gmail.com 166
first :: Lens' (a, b) a
first = lens getter setter
where
getter (a, _) = a
setter (_, b) a = (a, b)
λ> (10, 20) ^. first
10
λ> (10, 20) & first .~ 30
(30,20)
λ> (10, 20) & first .~ "hello"
<interactive>:25:2: error:
• Could not deduce (Num [Char]) arising from the literal ‘10’
from the context: Num b
@EncodePanda, paul.szulc@gmail.com 167
first :: Lens' (a, b) a
first = lens getter setter
where
getter (a, _) = a
setter (_, b) a = (a, b)
λ> (10, 20) ^. first
10
λ> (10, 20) & first .~ 30
(30,20)
λ> (10, 20) & first .~ "hello"
<interactive>:25:2: error:
• Could not deduce (Num [Char]) arising from the literal ‘10’
from the context: Num b
@EncodePanda, paul.szulc@gmail.com 168
first :: Lens (a, c) (b, c) a b
first = lens getter setter
where
getter (a, _) = a
setter (_, b) c = (c, b)
λ> (10, 20) ^. first
10
λ> (10, 20) & first .~ 30
(30,20)
λ> (10, 20) & first .~ "hello"
("hello",20)
@EncodePanda, paul.szulc@gmail.com 169
first :: Lens (a, c) (b, c) a b
first = lens getter setter
where
getter (a, _) = a
setter (_, b) c = (c, b)
λ> (10, 20) ^. first
10
λ> (10, 20) & first .~ 30
(30,20)
λ> (10, 20) & first .~ "hello"
("hello",20)
@EncodePanda, paul.szulc@gmail.com 170
data Truck a = Truck
{ name :: String
, cargo :: a
} deriving (Show, Generic)
data Fruit = Apple | Orange deriving Show
data Electronic = Computer | Phone deriving Show
λ> truck = Truck "Tom" Apple
truck :: Truck Fruit
λ> otherTruck = truck & #cargo .~ Phone
otherTruck :: Truck Electronic
@EncodePanda, paul.szulc@gmail.com 171
data Truck a = Truck
{ name :: String
, cargo :: a
} deriving (Show, Generic)
data Fruit = Apple | Orange deriving Show
data Electronic = Computer | Phone deriving Show
λ> truck = Truck "Tom" Apple
truck :: Truck Fruit
λ> otherTruck = truck & #cargo .~ Phone
otherTruck :: Truck Electronic
@EncodePanda, paul.szulc@gmail.com 172
data Truck a = Truck
{ name :: String
, cargo :: a
} deriving (Show, Generic)
data Fruit = Apple | Orange deriving Show
data Electronic = Computer | Phone deriving Show
λ> truck = Truck "Tom" Apple
truck :: Truck Fruit
λ> otherTruck = truck & #cargo .~ Phone
otherTruck :: Truck Electronic
@EncodePanda, paul.szulc@gmail.com 173
data Truck a = Truck
{ name :: String
, cargo :: a
} deriving (Show, Generic)
data Fruit = Apple | Orange deriving Show
data Electronic = Computer | Phone deriving Show
λ> truck = Truck "Tom" Apple
truck :: Truck Fruit
λ> otherTruck = truck & #cargo .~ Phone
otherTruck :: Truck Electronic
@EncodePanda, paul.szulc@gmail.com 174
λ> truck = Truck "tom" [Apple, Orange, Apple]
λ> truck & #cargo .~ (truck ^. #cargo <> [Orange, Apple])
Truck { name = "tom"
, cargo = [Apple,Orange,Apple,Orange,Apple]}
λ> truck & #cargo %~ (fruits -> fruits <> [Orange, Apple])
Truck { name = "tom"
, cargo = [Apple,Orange,Apple,Orange,Apple]}
λ> truck & #cargo <>~ [Orange, Apple]
Truck { name = "tom"
, cargo = [Apple,Orange,Apple,Orange,Apple]}
@EncodePanda, paul.szulc@gmail.com 175
λ> truck = Truck "tom" [Apple, Orange, Apple]
λ> truck & #cargo .~ (truck ^. #cargo <> [Orange, Apple])
Truck { name = "tom"
, cargo = [Apple,Orange,Apple,Orange,Apple]}
λ> truck & #cargo %~ (fruits -> fruits <> [Orange, Apple])
Truck { name = "tom"
, cargo = [Apple,Orange,Apple,Orange,Apple]}
λ> truck & #cargo <>~ [Orange, Apple]
Truck { name = "tom"
, cargo = [Apple,Orange,Apple,Orange,Apple]}
@EncodePanda, paul.szulc@gmail.com 176
λ> truck = Truck "tom" [Apple, Orange, Apple]
λ> truck & #cargo .~ (truck ^. #cargo <> [Orange, Apple])
Truck { name = "tom"
, cargo = [Apple,Orange,Apple,Orange,Apple]}
λ> truck & #cargo %~ (fruits -> fruits <> [Orange, Apple])
Truck { name = "tom"
, cargo = [Apple,Orange,Apple,Orange,Apple]}
λ> truck & #cargo <>~ [Orange, Apple]
Truck { name = "tom"
, cargo = [Apple,Orange,Apple,Orange,Apple]}
@EncodePanda, paul.szulc@gmail.com 177
λ> truck = Truck "tom" [Apple, Orange, Apple]
λ> truck & #cargo .~ (truck ^. #cargo <> [Orange, Apple])
Truck { name = "tom"
, cargo = [Apple,Orange,Apple,Orange,Apple]}
λ> truck & #cargo %~ (fruits -> fruits <> [Orange, Apple])
Truck { name = "tom"
, cargo = [Apple,Orange,Apple,Orange,Apple]}
λ> truck & #cargo <>~ [Orange, Apple]
Truck { name = "tom"
, cargo = [Apple,Orange,Apple,Orange,Apple]}
@EncodePanda, paul.szulc@gmail.com 178
λ> truck = Truck "tom" [Apple, Orange, Apple]
λ> truck & #cargo %~ length
& #cargo +~ 1
Truck {name = "tom", cargo = 4}
λ> truck & #cargo %~ length
& #cargo *~ 10
Truck {name = "tom", cargo = 30}
@EncodePanda, paul.szulc@gmail.com 179
Other Optics
@EncodePanda, paul.szulc@gmail.com 180
Traversal s t a b
@EncodePanda, paul.szulc@gmail.com 181
λ> haskellLove ^. #speakers
[ Speaker { name = Name {firstName = "Pawel", lastName = "Szulc"}
, slidesReady = False}
, Speaker { name = Name {firstName = "Marcin", lastName = "Rzeznicki"}
, slidesReady = True}
]
λ> haskellLove ^.. #speakers . traversed . #name . #lastName
["Szulc","Rzeznicki"]
λ> haskellLove ^. #speakers . traversed . #name . #lastName
"SzulcRzeznicki"
@EncodePanda, paul.szulc@gmail.com 182
λ> haskellLove ^. #speakers
[ Speaker { name = Name {firstName = "Pawel", lastName = "Szulc"}
, slidesReady = False}
, Speaker { name = Name {firstName = "Marcin", lastName = "Rzeznicki"}
, slidesReady = True}
]
λ> haskellLove ^.. #speakers . traversed . #name . #lastName
["Szulc","Rzeznicki"]
λ> haskellLove ^. #speakers . traversed . #name . #lastName
"SzulcRzeznicki"
@EncodePanda, paul.szulc@gmail.com 183
λ> haskellLove ^. #speakers
[ Speaker { name = Name {firstName = "Pawel", lastName = "Szulc"}
, slidesReady = False}
, Speaker { name = Name {firstName = "Marcin", lastName = "Rzeznicki"}
, slidesReady = True}
]
λ> haskellLove ^.. #speakers . traversed . #name . #lastName
["Szulc","Rzeznicki"]
λ> haskellLove ^. #speakers . traversed . #name . #lastName
"SzulcRzeznicki"
@EncodePanda, paul.szulc@gmail.com 184
Anyone ready?
anyOf :: Traversal s t a b -> (a -> Bool) -> s -> Bool
λ> anyOf (#speakers . traversed . #slidesReady) id haskellLove
True
@EncodePanda, paul.szulc@gmail.com 185
Anyone ready?
anyOf :: Traversal s t a b -> (a -> Bool) -> s -> Bool
λ> anyOf (#speakers . traversed . #slidesReady) id haskellLove
True
@EncodePanda, paul.szulc@gmail.com 186
λ> haskellLove & #speakers %~ (fmap (set #slidesReady False))
Conference
{ name = "Haskell.Love"
, organizer = Organizer {..}
, speakers = [ Speaker { name = Name { firstName = "Pawel"
, lastName = "Szulc"}
, slidesReady = False}
, Speaker { name = Name { firstName = "Marcin"
, lastName = "Rzeznicki"}
, slidesReady = False}
]
}
@EncodePanda, paul.szulc@gmail.com 187
λ> haskellLove & #speakers . traversed . #slidesReady .~ False
Conference
{ name = "Haskell.Love"
, organizer = Organizer {..}
, speakers = [ Speaker { name = Name { firstName = "Pawel"
, lastName = "Szulc"}
, slidesReady = False}
, Speaker { name = Name { firstName = "Marcin"
, lastName = "Rzeznicki"}
, slidesReady = False}
]
}
@EncodePanda, paul.szulc@gmail.com 188
Reveal the magic
@EncodePanda, paul.szulc@gmail.com 189
Exhibit A: Lens s t a b
1. How to encode getter and setter in one type?
2. How to compose via "dot"?
@EncodePanda, paul.szulc@gmail.com 190
data Lenz s t a b = Lenz
{ view :: s -> a
, set :: s -> b -> t
}
over :: Lenz s t a b -> (a -> b) -> s -> t
over lenz func s =
let
old = view lenz s
new = func old
in
set lenz s new
type Lenz' s a = Lenz s s a a
@EncodePanda, paul.szulc@gmail.com 191
data Lenz s t a b = Lenz
{ view :: s -> a
, set :: s -> b -> t
}
over :: Lenz s t a b -> (a -> b) -> s -> t
over lenz func s =
let
old = view lenz s
new = func old
in
set lenz s new
type Lenz' s a = Lenz s s a a
@EncodePanda, paul.szulc@gmail.com 192
data Lenz s t a b = Lenz
{ view :: s -> a
, set :: s -> b -> t
}
over :: Lenz s t a b -> (a -> b) -> s -> t
over lenz func s =
let
old = view lenz s
new = func old
in
set lenz s new
type Lenz' s a = Lenz s s a a
@EncodePanda, paul.szulc@gmail.com 193
But that "dot" though...
@EncodePanda, paul.szulc@gmail.com 194
data Lenz s t a b = Lenz
{ view :: s -> a
, set :: s -> b -> t
}
@EncodePanda, paul.szulc@gmail.com 195
data Lenz s t a b = Lenz
{ lenz :: s -> (a, b -> t)
}
@EncodePanda, paul.szulc@gmail.com 196
type Lenz s t a b = s -> (a, b -> t)
@EncodePanda, paul.szulc@gmail.com 197
type Lenz s t a b = s -> (a, b -> t)
But this will not compose with itself.
@EncodePanda, paul.szulc@gmail.com 198
type Lens s t a b =
forall f. Functor f =>
(a -> f b) -> s -> f t
@EncodePanda, paul.szulc@gmail.com 199
@EncodePanda, paul.szulc@gmail.com 200
type Lens s t a b =
forall f. Functor f =>
(a -> f b) -> s -> f t
@EncodePanda, paul.szulc@gmail.com 201
type Lens s t a b =
forall f. Functor f =>
(a -> f b) -> s -> f t
organizer :: Lens' Conference Organizer
contact :: Lens' Organizer Contact
forall f. Functor f =>
(Organizer -> f Organizer) -> (Conference -> f Conference)
forall f. Functor f =>
(Contact-> f Contact) -> (Organizer -> Organizer)
@EncodePanda, paul.szulc@gmail.com 202
type Lens s t a b =
forall f. Functor f =>
(a -> f b) -> s -> f t
organizer :: Lens' Conference Organizer
contact :: Lens' Organizer Contact
forall f. Functor f =>
(Organizer -> f Organizer) -> (Conference -> f Conference)
forall f. Functor f =>
(Contact-> f Contact) -> (Organizer -> Organizer)
@EncodePanda, paul.szulc@gmail.com 203
type Lens s t a b =
forall f. Functor f =>
(a -> f b) -> s -> f t
organizer :: Lens' Conference Organizer
contact :: Lens' Organizer Contact
forall f. Functor f =>
(Organizer -> f Organizer) -> (Conference -> f Conference)
forall f. Functor f =>
(Contact-> f Contact) -> (Organizer -> Organizer)
@EncodePanda, paul.szulc@gmail.com 204
type Lens s t a b =
forall f. Functor f =>
(a -> f b) -> s -> f t
organizer :: Lens' Conference Organizer
contact :: Lens' Organizer Contact
forall f. Functor f =>
(Organizer -> f Organizer) -> (Conference -> f Conference)
forall f. Functor f =>
(Contact-> f Contact) -> (Organizer -> Organizer)
:t organizer . contact
forall f. Functor f =>
(Contact-> f Contact) -> (Conference-> Conference)
@EncodePanda, paul.szulc@gmail.com 205
type Lens s t a b =
forall f. Functor f =>
(a -> f b) -> s -> f t
@EncodePanda, paul.szulc@gmail.com 206
type Lens s t a b =
forall f. Functor f =>
(a -> f b) -> s -> f t
over :: Lens s t a b -> (a -> b) -> s -> t
set :: Lens s t a b -> b -> s -> t
view :: Lens s t a b -> s -> a
@EncodePanda, paul.szulc@gmail.com 207
type Lens s t a b =
forall f. Functor f =>
(a -> f b) -> s -> f t
over :: Lens s t a b -> (a -> b) -> s -> t
set :: Lens s t a b -> b -> s -> t
view :: Lens s t a b -> s -> a
@EncodePanda, paul.szulc@gmail.com 208
type Lens s t a b =
forall f. Functor f =>
(a -> f b) -> s -> f t
over :: Lens s t a b -> (a -> b) -> s -> t
set :: Lens s t a b -> b -> s -> t
view :: Lens s t a b -> s -> a
@EncodePanda, paul.szulc@gmail.com 209
type Lens s t a b =
forall f. Functor f =>
(a -> f b) -> s -> f t
over :: Lens s t a b -> (a -> b) -> s -> t
set :: Lens s t a b -> b -> s -> t
view :: Lens s t a b -> s -> a
@EncodePanda, paul.szulc@gmail.com 210
type Lens s t a b =
forall f. Functor f =>
(a -> f b) -> s -> f t
over :: Lens s t a b -> (a -> b) -> s -> t
over l func s =
@EncodePanda, paul.szulc@gmail.com 211
type Lens s t a b =
forall f. Functor f =>
(a -> f b) -> s -> f t
over :: Lens s t a b -> (a -> b) -> s -> t
over l func s = l (Identity . func) s
@EncodePanda, paul.szulc@gmail.com 212
type Lens s t a b =
forall f. Functor f =>
(a -> f b) -> s -> f t
over :: Lens s t a b -> (a -> b) -> s -> t
over l func s = runIdentity . l (Identity . func) $ s
@EncodePanda, paul.szulc@gmail.com 213
type Lens s t a b =
forall f. Functor f =>
(a -> f b) -> s -> f t
over :: Lens s t a b -> (a -> b) -> s -> t
set :: Lens s t a b -> b -> s -> t
set l b s = over l (const b) s
@EncodePanda, paul.szulc@gmail.com 214
type Lens s t a b =
forall f. Functor f =>
(a -> f b) -> s -> f t
view :: Lens s t a b -> s -> a
@EncodePanda, paul.szulc@gmail.com 215
newtype Const a b = Const { getConst :: a }
instance Functor (Const m) where
fmap _ (Const v) = Const v
@EncodePanda, paul.szulc@gmail.com 216
type Lens s t a b =
forall f. Functor f =>
(a -> f b) -> s -> f t
view :: Lens s t a b -> s -> a
@EncodePanda, paul.szulc@gmail.com 217
type Lens s t a b =
forall f. Functor f =>
(a -> f b) -> s -> f t
view :: Lens s t a b -> s -> a
view l s = l Const s
@EncodePanda, paul.szulc@gmail.com 218
type Lens s t a b =
forall f. Functor f =>
(a -> f b) -> s -> f t
view :: Lens s t a b -> s -> a
view l s = getConst . l Const $ s
@EncodePanda, paul.szulc@gmail.com 219
view :: Lens s t a b -> s -> a
view l s = getConst . l
Const $ s
@EncodePanda, paul.szulc@gmail.com 220
@EncodePanda, paul.szulc@gmail.com 221
@EncodePanda, paul.szulc@gmail.com 222
@EncodePanda, paul.szulc@gmail.com 223
@EncodePanda, paul.szulc@gmail.com 224
@EncodePanda (Pawel Szulc)
@EncodePanda, paul.szulc@gmail.com 225
Thank you!
@EncodePanda, paul.szulc@gmail.com 226

More Related Content

KEY
Let's Have a Cup of CoffeeScript
PDF
Scope Graphs: A fresh look at name binding in programming languages
PDF
Static name resolution
PDF
Getting rid of backtracking
PDF
Name binding with scope graphs
PDF
Compiler Construction | Lecture 8 | Type Constraints
PDF
Munihac 2018 - Beautiful Template Haskell
PDF
Type safe embedded domain-specific languages
Let's Have a Cup of CoffeeScript
Scope Graphs: A fresh look at name binding in programming languages
Static name resolution
Getting rid of backtracking
Name binding with scope graphs
Compiler Construction | Lecture 8 | Type Constraints
Munihac 2018 - Beautiful Template Haskell
Type safe embedded domain-specific languages

Similar to Getting acquainted with Lens (20)

PDF
Transpilers Gone Wild: Introducing Hydra
PDF
Petitparser at the Deep into Smalltalk School 2011
PDF
Евгений Курбацкий. Зачем нужны зависимые типы
PPT
Extractors & Implicit conversions
PDF
Declare Your Language (at DLS)
PDF
Galois Tech Talk / Vinyl: Records in Haskell and Type Theory
PPT
Pl vol1
PPTX
Dsm as theory building
PDF
Model-Driven Software Development - Static Analysis & Error Checking
PDF
Dynamic Semantics
PDF
Declare Your Language: Type Checking
PPT
Pl vol1
PDF
Declarative Type System Specification with Statix
PDF
The Magnificent Seven
PPTX
Introduction to compiler construction
PDF
Scala jargon cheatsheet
PDF
CS-4337_03_Chapter3- syntax and semantics.pdf
PDF
Clojure for Java developers - Stockholm
PDF
js+ts fullstack typescript with react and express.pdf
Transpilers Gone Wild: Introducing Hydra
Petitparser at the Deep into Smalltalk School 2011
Евгений Курбацкий. Зачем нужны зависимые типы
Extractors & Implicit conversions
Declare Your Language (at DLS)
Galois Tech Talk / Vinyl: Records in Haskell and Type Theory
Pl vol1
Dsm as theory building
Model-Driven Software Development - Static Analysis & Error Checking
Dynamic Semantics
Declare Your Language: Type Checking
Pl vol1
Declarative Type System Specification with Statix
The Magnificent Seven
Introduction to compiler construction
Scala jargon cheatsheet
CS-4337_03_Chapter3- syntax and semantics.pdf
Clojure for Java developers - Stockholm
js+ts fullstack typescript with react and express.pdf
Ad

More from Pawel Szulc (20)

PDF
Impossibility
PDF
Maintainable Software Architecture in Haskell (with Polysemy)
PDF
Painless Haskell
PDF
Trip with monads
PDF
Trip with monads
PDF
Illogical engineers
PDF
RChain - Understanding Distributed Calculi
PDF
Illogical engineers
PDF
Understanding distributed calculi in Haskell
PDF
Software engineering the genesis
PDF
Make your programs Free
PDF
Going bananas with recursion schemes for fixed point data types
PDF
“Going bananas with recursion schemes for fixed point data types”
PDF
Writing your own RDD for fun and profit
PDF
The cats toolbox a quick tour of some basic typeclasses
PDF
Introduction to type classes
PDF
Functional Programming & Event Sourcing - a pair made in heaven
PDF
Apache spark workshop
PDF
Introduction to type classes in 30 min
PDF
Real world gobbledygook
Impossibility
Maintainable Software Architecture in Haskell (with Polysemy)
Painless Haskell
Trip with monads
Trip with monads
Illogical engineers
RChain - Understanding Distributed Calculi
Illogical engineers
Understanding distributed calculi in Haskell
Software engineering the genesis
Make your programs Free
Going bananas with recursion schemes for fixed point data types
“Going bananas with recursion schemes for fixed point data types”
Writing your own RDD for fun and profit
The cats toolbox a quick tour of some basic typeclasses
Introduction to type classes
Functional Programming & Event Sourcing - a pair made in heaven
Apache spark workshop
Introduction to type classes in 30 min
Real world gobbledygook
Ad

Recently uploaded (20)

PDF
Univ-Connecticut-ChatGPT-Presentaion.pdf
PPTX
OMC Textile Division Presentation 2021.pptx
PDF
Zenith AI: Advanced Artificial Intelligence
PDF
ENT215_Completing-a-large-scale-migration-and-modernization-with-AWS.pdf
PDF
Hindi spoken digit analysis for native and non-native speakers
PDF
Encapsulation_ Review paper, used for researhc scholars
PDF
Heart disease approach using modified random forest and particle swarm optimi...
PPTX
Tartificialntelligence_presentation.pptx
PDF
A comparative study of natural language inference in Swahili using monolingua...
PDF
Mushroom cultivation and it's methods.pdf
PPTX
Programs and apps: productivity, graphics, security and other tools
PDF
MIND Revenue Release Quarter 2 2025 Press Release
PDF
DASA ADMISSION 2024_FirstRound_FirstRank_LastRank.pdf
PDF
Encapsulation theory and applications.pdf
PPTX
Chapter 5: Probability Theory and Statistics
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PDF
gpt5_lecture_notes_comprehensive_20250812015547.pdf
PDF
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
PPTX
A Presentation on Touch Screen Technology
PDF
Approach and Philosophy of On baking technology
Univ-Connecticut-ChatGPT-Presentaion.pdf
OMC Textile Division Presentation 2021.pptx
Zenith AI: Advanced Artificial Intelligence
ENT215_Completing-a-large-scale-migration-and-modernization-with-AWS.pdf
Hindi spoken digit analysis for native and non-native speakers
Encapsulation_ Review paper, used for researhc scholars
Heart disease approach using modified random forest and particle swarm optimi...
Tartificialntelligence_presentation.pptx
A comparative study of natural language inference in Swahili using monolingua...
Mushroom cultivation and it's methods.pdf
Programs and apps: productivity, graphics, security and other tools
MIND Revenue Release Quarter 2 2025 Press Release
DASA ADMISSION 2024_FirstRound_FirstRank_LastRank.pdf
Encapsulation theory and applications.pdf
Chapter 5: Probability Theory and Statistics
Building Integrated photovoltaic BIPV_UPV.pdf
gpt5_lecture_notes_comprehensive_20250812015547.pdf
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
A Presentation on Touch Screen Technology
Approach and Philosophy of On baking technology

Getting acquainted with Lens

  • 1. "Getting acquainted with Lens" @EncodePanda @EncodePanda, paul.szulc@gmail.com 1
  • 2. This talk is about Lens1 1. What problem they are trying to solve? 2. How we can use them? 3. How they are being implemented? 1  https://hackage.haskell.org/package/lens. @EncodePanda, paul.szulc@gmail.com 2
  • 3. There are two things fundamentally broken in Haskell 1. Record syntax 2. Strings @EncodePanda, paul.szulc@gmail.com 3
  • 4. There are two things fundamentally broken in Haskell 1. Record syntax 2. Strings @EncodePanda, paul.szulc@gmail.com 4
  • 5. Record syntax broken? "The record system is a continual source of pain" - Stephen Diehl @EncodePanda, paul.szulc@gmail.com 5
  • 6. Record syntax broken? "What is your least favorite thing about Haskell? Records are still tedious" - 2018 State of Haskell Survey @EncodePanda, paul.szulc@gmail.com 6
  • 7. Record syntax broken? "Shitty records." - Someone on reddit @EncodePanda, paul.szulc@gmail.com 7
  • 9. data Conference = Conference { organizer :: Organizer , speakers :: [Speaker] } deriving Show @EncodePanda, paul.szulc@gmail.com 9
  • 10. data Conference = Conference { organizer :: Organizer , speakers :: [Speaker] } deriving Show data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show @EncodePanda, paul.szulc@gmail.com 10
  • 11. data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show @EncodePanda, paul.szulc@gmail.com 11
  • 12. data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show data Name = Name { firstName :: String , lastName :: String } deriving Show @EncodePanda, paul.szulc@gmail.com 12
  • 13. data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show data Contact = Contact { address :: Address , email :: String } deriving Show @EncodePanda, paul.szulc@gmail.com 13
  • 14. data Address = Address { street :: String , city :: String , country :: String } deriving Show @EncodePanda, paul.szulc@gmail.com 14
  • 15. data Conference = Conference { organizer :: Organizer , speakers :: [Speaker] } deriving Show @EncodePanda, paul.szulc@gmail.com 15
  • 16. data Conference = Conference { organizer :: Organizer , speakers :: [Speaker] } deriving Show data Speaker = Speaker { slidesReady :: Bool } deriving Show @EncodePanda, paul.szulc@gmail.com 16
  • 17. Getting things from a record @EncodePanda, paul.szulc@gmail.com 17
  • 18. data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show oli :: Organizer oli = Organizer { name = Name "Oli" "Makhasoeva" , contact = classified } @EncodePanda, paul.szulc@gmail.com 18
  • 19. data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show oli :: Organizer oli = Organizer { name = Name "Oli" "Makhasoeva" , contact = classified } @EncodePanda, paul.szulc@gmail.com 19
  • 20. data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show λ> :t name name :: Organizer -> Name @EncodePanda, paul.szulc@gmail.com 20
  • 21. data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show λ> name oli Name {firstName = "Oli", lastName = "Makhasoeva"} @EncodePanda, paul.szulc@gmail.com 21
  • 22. import Data.Function ((&)) λ> :t (&) (&) :: a -> (a -> b) -> b λ> length [4, 6, 8] 3 λ> [4, 6, 8] & length 3 @EncodePanda, paul.szulc@gmail.com 22
  • 23. import Data.Function ((&)) λ> :t (&) (&) :: a -> (a -> b) -> b λ> length [4, 6, 8] 3 λ> [4, 6, 8] & length 3 @EncodePanda, paul.szulc@gmail.com 23
  • 24. import Data.Function ((&)) λ> :t (&) (&) :: a -> (a -> b) -> b λ> length [4, 6, 8] 3 λ> [4, 6, 8] & length 3 @EncodePanda, paul.szulc@gmail.com 24
  • 25. import Data.Function ((&)) λ> :t (&) (&) :: a -> (a -> b) -> b λ> length [4, 6, 8] 3 λ> [4, 6, 8] & length 3 @EncodePanda, paul.szulc@gmail.com 25
  • 26. data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show λ> name oli Name {firstName = "Oli", lastName = "Makhasoeva"} @EncodePanda, paul.szulc@gmail.com 26
  • 27. data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show λ> oli & name Name {firstName = "Oli", lastName = "Makhasoeva"} @EncodePanda, paul.szulc@gmail.com 27
  • 28. data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show λ> oli & name & firstName "Oli" @EncodePanda, paul.szulc@gmail.com 28
  • 29. organizerCountry :: Conference -> String organizerCountry conf = conf & organizer & contact & address & country @EncodePanda, paul.szulc@gmail.com 29
  • 31. data Conference = Conference { organizer :: Organizer , speakers :: [Speaker] } deriving Show data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show data Speaker = Speaker { slidesReady :: Bool } deriving Show @EncodePanda, paul.szulc@gmail.com 31
  • 32. data Conference = Conference { name :: String , organizer :: Organizer , speakers :: [Speaker] } deriving Show data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show data Speaker = Speaker { name :: Name , slidesReady :: Bool } deriving Show @EncodePanda, paul.szulc@gmail.com 32
  • 33. data Conference = Conference { name :: String , organizer :: Organizer , speakers :: [Speaker] } deriving Show data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show data Speaker = Speaker { name :: Name , slidesReady :: Bool } deriving Show @EncodePanda, paul.szulc@gmail.com 33
  • 34. /../src/WhyLens.hs:27:5: error: Multiple declarations of ‘name’ Declared at: src/WhyLens.hs:12:5 src/WhyLens.hs:27:5 | 27 | { name :: Name | @EncodePanda, paul.szulc@gmail.com 34
  • 35. We can actually do something about it @EncodePanda, paul.szulc@gmail.com 35
  • 37. {-# LANGUAGE DuplicateRecordFields #-} @EncodePanda, paul.szulc@gmail.com 37
  • 38. {-# LANGUAGE DuplicateRecordFields #-} module WhyLens where data Conference = Conference { name :: String , organizer :: Organizer , speakers :: [Speaker] } deriving Show data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show data Speaker = Speaker { name :: Name , slidesReady :: Bool } deriving Show @EncodePanda, paul.szulc@gmail.com 38
  • 39. {-# LANGUAGE DuplicateRecordFields #-} module WhyLens where data Conference = Conference { name :: String , organizer :: Organizer , speakers :: [Speaker] } deriving Show data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show data Speaker = Speaker { name :: Name , slidesReady :: Bool } deriving Show @EncodePanda, paul.szulc@gmail.com 39
  • 41. organizerName :: Conference -> Name organizerName conference = conference & organizer & name @EncodePanda, paul.szulc@gmail.com 41
  • 42. /../src/WhyLens.hs:69:28: error: Ambiguous occurrence ‘name’ It could refer to either the field ‘name’, defined at src/WhyLens.hs:34:5 or the field ‘name’, defined at src/WhyLens.hs:19:5 or the field ‘name’, defined at src/WhyLens.hs:13:5 @EncodePanda, paul.szulc@gmail.com 42
  • 44. {-# LANGUAGE OverloadedLabels #-} @EncodePanda, paul.szulc@gmail.com 44
  • 46. {-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedLabels #-} @EncodePanda, paul.szulc@gmail.com 46
  • 47. class IsLabel (x :: Symbol) a where fromLabel :: a instance IsLabel "encodepanda" Speaker where fromLabel = Speaker { name = Name "Pawel" "Szulc" , slidesReady = False } pawel :: Speaker pawel = fromLabel @"encodepanda" @EncodePanda, paul.szulc@gmail.com 47
  • 48. class IsLabel (x :: Symbol) a where fromLabel :: a instance IsLabel "encodepanda" Speaker where fromLabel = Speaker { name = Name "Pawel" "Szulc" , slidesReady = False } pawel :: Speaker pawel = fromLabel @"encodepanda" @EncodePanda, paul.szulc@gmail.com 48
  • 49. class IsLabel (x :: Symbol) a where fromLabel :: a instance IsLabel "encodepanda" Speaker where fromLabel = Speaker { name = Name "Pawel" "Szulc" , slidesReady = False } pawel :: Speaker pawel = fromLabel @"encodepanda" @EncodePanda, paul.szulc@gmail.com 49
  • 50. class IsLabel (x :: Symbol) a where fromLabel :: a instance IsLabel "encodepanda" Speaker where fromLabel = Speaker { name = Name "Pawel" "Szulc" , slidesReady = False } pawel :: Speaker pawel = fromLabel @"encodepanda" @EncodePanda, paul.szulc@gmail.com 50
  • 51. class IsLabel (x :: Symbol) a where fromLabel :: a instance IsLabel "encodepanda" Speaker where fromLabel = Speaker { name = Name "Pawel" "Szulc" , slidesReady = False } pawel :: Speaker pawel = #encodepanda @EncodePanda, paul.szulc@gmail.com 51
  • 52. organizerName :: Conference -> Name organizerName conference = conference & organizer & name @EncodePanda, paul.szulc@gmail.com 52
  • 53. /../src/WhyLens.hs:69:28: error: Ambiguous occurrence ‘name’ It could refer to either the field ‘name’, defined at src/WhyLens.hs:34:5 or the field ‘name’, defined at src/WhyLens.hs:19:5 or the field ‘name’, defined at src/WhyLens.hs:13:5 @EncodePanda, paul.szulc@gmail.com 53
  • 54. data Conference = Conference { name :: String , organizer :: Organizer , speakers :: [Speaker] } deriving Show data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show data Speaker = Speaker { name :: Name , slidesReady :: Bool } deriving Show @EncodePanda, paul.szulc@gmail.com 54
  • 55. data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show instance IsLabel "name" (Organizer -> Name) where fromLabel = name @EncodePanda, paul.szulc@gmail.com 55
  • 56. data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show instance IsLabel "name" (Organizer -> Name) where fromLabel = name @EncodePanda, paul.szulc@gmail.com 56
  • 57. data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show instance IsLabel "name" (Organizer -> Name) where fromLabel = name organizerName :: Conference -> Name organizerName conference = conference & organizer & name @EncodePanda, paul.szulc@gmail.com 57
  • 58. data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show instance IsLabel "name" (Organizer -> Name) where fromLabel = name organizerName :: Conference -> Name organizerName conference = conference & organizer & #name @EncodePanda, paul.szulc@gmail.com 58
  • 61. Setting things to a record @EncodePanda, paul.szulc@gmail.com 61
  • 62. λ> haskellLove Conference { name = "Haskell.Love" , organizer = Organizer {..} , speakers = [] } @EncodePanda, paul.szulc@gmail.com 62
  • 63. λ> haskellLove Conference { name = "Haskell.Love" , organizer = Organizer {..} , speakers = [] } @EncodePanda, paul.szulc@gmail.com 63
  • 64. λ> haskellLove { speakers = [ pawel, marcin ] } Conference { name = "Haskell.Love" , organizer = Organizer {..} , speakers = [ Speaker { name = Name { firstName = "Pawel" , lastName = "Szulc"} , slidesReady = False } , Speaker { name = Name { firstName = "Marcin" , lastName = "Rzeznicki"} , slidesReady = False} ] } @EncodePanda, paul.szulc@gmail.com 64
  • 65. λ> haskellLove { speakers = [ pawel, marcin ] } Conference { name = "Haskell.Love" , organizer = Organizer {..} , speakers = [ Speaker { name = Name { firstName = "Pawel" , lastName = "Szulc"} , slidesReady = False } , Speaker { name = Name { firstName = "Marcin" , lastName = "Rzeznicki"} , slidesReady = False} ] } @EncodePanda, paul.szulc@gmail.com 65
  • 67. allSpeakersNotReady :: Conference -> Conference allSpeakersNotReady conference = let oldSpeakers = conference & speakers in conference { speakers = fmap (s -> s { slidesReady = False}) oldSpeakers } @EncodePanda, paul.szulc@gmail.com 67
  • 68. allSpeakersNotReady :: Conference -> Conference allSpeakersNotReady conference = let oldSpeakers = conference & speakers in conference { speakers = fmap (s -> s { slidesReady = False}) oldSpeakers } @EncodePanda, paul.szulc@gmail.com 68
  • 69. allSpeakersNotReady :: Conference -> Conference allSpeakersNotReady conference = let oldSpeakers = conference & speakers in conference { speakers = fmap (s -> s { slidesReady = False}) oldSpeakers } @EncodePanda, paul.szulc@gmail.com 69
  • 70. changeOrganizerEmail :: (String -> String) -> Conference -> Conference changeOrganizerEmail modifyEmail conference = let oldOrganizer = conference & organizer newContact = (oldOrganizer & contact) { email = modifyEmail (oldOrganizer & contact & email) } newOrganizer = oldOrganizer { contact = newContact} in conference { organizer = newOrganizer } @EncodePanda, paul.szulc@gmail.com 70
  • 71. changeOrganizerEmail :: (String -> String) -> Conference -> Conference changeOrganizerEmail modifyEmail conference = let oldOrganizer = conference & organizer newContact = (oldOrganizer & contact) { email = modifyEmail (oldOrganizer & contact & email) } newOrganizer = oldOrganizer { contact = newContact} in conference { organizer = newOrganizer } @EncodePanda, paul.szulc@gmail.com 71
  • 72. changeOrganizerEmail :: (String -> String) -> Conference -> Conference changeOrganizerEmail modifyEmail conference = let oldOrganizer = conference & organizer newContact = (oldOrganizer & contact) { email = modifyEmail (oldOrganizer & contact & email) } newOrganizer = oldOrganizer { contact = newContact} in conference { organizer = newOrganizer } @EncodePanda, paul.szulc@gmail.com 72
  • 73. changeOrganizerEmail :: (String -> String) -> Conference -> Conference changeOrganizerEmail modifyEmail conference = let oldOrganizer = conference & organizer newContact = (oldOrganizer & contact) { email = modifyEmail (oldOrganizer & contact & email) } newOrganizer = oldOrganizer { contact = newContact} in conference { organizer = newOrganizer } @EncodePanda, paul.szulc@gmail.com 73
  • 74. changeOrganizerEmail :: (String -> String) -> Conference -> Conference changeOrganizerEmail modifyEmail conference = let oldOrganizer = conference & organizer newContact = (oldOrganizer & contact) { email = modifyEmail (oldOrganizer & contact & email) } newOrganizer = oldOrganizer { contact = newContact} in conference { organizer = newOrganizer } @EncodePanda, paul.szulc@gmail.com 74
  • 75. changeOrganizerEmail :: (String -> String) -> Conference -> Conference changeOrganizerEmail modifyEmail conference = let oldOrganizer = conference & organizer newContact = (oldOrganizer & contact) { email = modifyEmail (oldOrganizer & contact & email) } newOrganizer = oldOrganizer { contact = newContact} in conference { organizer = newOrganizer } @EncodePanda, paul.szulc@gmail.com 75
  • 77. What can you do about it 1. Change the programming language 2. Quit 3. Ask the Oracle @EncodePanda, paul.szulc@gmail.com 77
  • 104. Lens' s a @EncodePanda, paul.szulc@gmail.com 104
  • 105. _organizer :: Lens' Conference Organizer @EncodePanda, paul.szulc@gmail.com 105
  • 106. _organizer :: Lens' Conference Organizer λ> view _organizer haskellLove Organizer { name = Name { firstName = "Oli" , lastName = "Makhasoeva"} , contact = Contact {..} } @EncodePanda, paul.szulc@gmail.com 106
  • 107. _organizer :: Lens' Conference Organizer λ> haskellLove ^. _organizer Organizer { name = Name { firstName = "Oli" , lastName = "Makhasoeva"} , contact = Contact {..} } @EncodePanda, paul.szulc@gmail.com 107
  • 108. _organizer :: Lens' Conference Organizer _contact :: Lens' Organizer Contact _address :: Lens' Contact Address _country :: Lens' Address String λ> haskelllove ^. _organizer . _contact . _address . _country "classified" @EncodePanda, paul.szulc@gmail.com 108
  • 109. _organizer :: Lens' Conference Organizer _contact :: Lens' Organizer Contact _address :: Lens' Contact Address _country :: Lens' Address String λ> haskelllove ^. _organizer . _contact . _address . _country "classified" @EncodePanda, paul.szulc@gmail.com 109
  • 110. _organizer :: Lens' Conference Organizer _contact :: Lens' Organizer Contact _address :: Lens' Contact Address _country :: Lens' Address String λ> haskelllove ^. _organizer . _contact . _address . _country "classified" @EncodePanda, paul.szulc@gmail.com 110
  • 111. _organizer :: Lens' Conference Organizer _contact :: Lens' Organizer Contact _address :: Lens' Contact Address _country :: Lens' Address String λ> haskelllove ^. _organizer . _contact . _address . _country "classified" @EncodePanda, paul.szulc@gmail.com 111
  • 113. (.) :: (b -> c) -> (a -> b) -> a -> c How is that possible? @EncodePanda, paul.szulc@gmail.com 113
  • 115. _organizer :: Lens' Conference Organizer _contact :: Lens' Organizer Contact _address :: Lens' Contact Address _country :: Lens' Address String λ> haskelllove ^. _organizer . _contact . _address . _country "classified" @EncodePanda, paul.szulc@gmail.com 115
  • 116. _name :: Lens' Conference String λ> set _name "Haskell Love 2020" haskellLove Conference { name = "Haskell Love 2020" , organizer = Organizer {..} , speakers = [..] } @EncodePanda, paul.szulc@gmail.com 116
  • 117. _name :: Lens' Conference String λ> (_name .~ "Haskell Love 2020") haskellLove Conference { name = "Haskell Love 2020" , organizer = Organizer {..} , speakers = [..] } @EncodePanda, paul.szulc@gmail.com 117
  • 118. _name :: Lens' Conference String λ> haskellLove & _name .~ "Haskell Love 2020" Conference { name = "Haskell Love 2020" , organizer = Organizer {..} , speakers = [..] } @EncodePanda, paul.szulc@gmail.com 118
  • 119. _organizer :: Lens' Conference Organizer _contact :: Lens' Organizer Contact _address :: Lens' Contact Address _country :: Lens' Address String λ> haskellLove & _organizer . _contact . _address . _country .~ "Poland" Conference { name = "Haskell.Love" , organizer = Organizer { name = Name {firstName = "Oli", lastName = "Makhasoeva"} , contact = Contact { address = Address { street = "Class" , city = "ified" , country = "Poland" } , email = "oli@haskell.love"} } , speakers = [...] } @EncodePanda, paul.szulc@gmail.com 119
  • 120. _organizer :: Lens' Conference Organizer _contact :: Lens' Organizer Contact _address :: Lens' Contact Address _country :: Lens' Address String λ> haskellLove & _organizer . _contact . _address . _country .~ "Poland" Conference { name = "Haskell.Love" , organizer = Organizer { name = Name {firstName = "Oli", lastName = "Makhasoeva"} , contact = Contact { address = Address { street = "Class" , city = "ified" , country = "Poland" } , email = "oli@haskell.love"} } , speakers = [...] } @EncodePanda, paul.szulc@gmail.com 120
  • 121. _organizer :: Lens' Conference Organizer _contact :: Lens' Organizer Contact _address :: Lens' Contact Address _country :: Lens' Address String λ> haskellLove & _organizer . _contact . _address . _country .~ "Poland" Conference { name = "Haskell.Love" , organizer = Organizer { name = Name {firstName = "Oli", lastName = "Makhasoeva"} , contact = Contact { address = Address { street = "Class" , city = "ified" , country = "Poland" } , email = "oli@haskell.love"} } , speakers = [...] } @EncodePanda, paul.szulc@gmail.com 121
  • 122. _organizer :: Lens' Conference Organizer _contact :: Lens' Organizer Contact _address :: Lens' Contact Address _country :: Lens' Address String λ> haskellLove & _organizer . _contact . _address . _country .~ "Poland" Conference { name = "Haskell.Love" , organizer = Organizer { name = Name {firstName = "Oli", lastName = "Makhasoeva"} , contact = Contact { address = Address { street = "Class" , city = "ified" , country = "Poland" } , email = "oli@haskell.love"} } , speakers = [...] } @EncodePanda, paul.szulc@gmail.com 122
  • 123. _organizer :: Lens' Conference Organizer _contact :: Lens' Organizer Contact _address :: Lens' Contact Address _country :: Lens' Address String λ> haskellLove & _organizer . _contact . _address . _country .~ "Poland" Conference { name = "Haskell.Love" , organizer = Organizer { name = Name {firstName = "Oli", lastName = "Makhasoeva"} , contact = Contact { address = Address { street = "Class" , city = "ified" , country = "Poland" } , email = "oli@haskell.love"} } , speakers = [...] } @EncodePanda, paul.szulc@gmail.com 123
  • 124. _organizer :: Lens' Conference Organizer _contact :: Lens' Organizer Contact _address :: Lens' Contact Address _country :: Lens' Address String λ> haskellLove & _organizer . _contact . _address . _country .~ "Poland" & _name .~ "Haskell Love 2021" Conference { name = "Haskell Love 2021" , organizer = Organizer { name = Name {firstName = "Oli", lastName = "Makhasoeva"} , contact = Contact { address = Address { street = "Class" , city = "ified" , country = "Poland" } , email = "oli@haskell.love"} } , speakers = [...]} @EncodePanda, paul.szulc@gmail.com 124
  • 125. _organizer :: Lens' Conference Organizer _contact :: Lens' Organizer Contact _address :: Lens' Contact Address _country :: Lens' Address String λ> haskellLove & _organizer . _contact . _address . _country .~ "Poland" & _name .~ "Haskell Love 2021" Conference { name = "Haskell Love 2021" , organizer = Organizer { name = Name {firstName = "Oli", lastName = "Makhasoeva"} , contact = Contact { address = Address { street = "Class" , city = "ified" , country = "Poland" } , email = "oli@haskell.love"} } , speakers = [...]} @EncodePanda, paul.szulc@gmail.com 125
  • 126. _organizer :: Lens' Conference Organizer _contact :: Lens' Organizer Contact _address :: Lens' Contact Address _country :: Lens' Address String λ> haskellLove & _organizer . _contact . _address . _country .~ "Poland" & _name .~ "Haskell Love 2021" Conference { name = "Haskell Love 2021" , organizer = Organizer { name = Name {firstName = "Oli", lastName = "Makhasoeva"} , contact = Contact { address = Address { street = "Class" , city = "ified" , country = "Poland" } , email = "oli@haskell.love"} } , speakers = [...]} @EncodePanda, paul.szulc@gmail.com 126
  • 127. _organizer :: Lens' Conference Organizer _contact :: Lens' Organizer Contact _address :: Lens' Contact Address _country :: Lens' Address String λ> :t lens lens :: (s -> a) -> (s -> a -> s) -> Lens' s a @EncodePanda, paul.szulc@gmail.com 127
  • 128. _organizer :: Lens' Conference Organizer _contact :: Lens' Organizer Contact _address :: Lens' Contact Address _country :: Lens' Address String λ> :t lens lens :: (s -> a) -> (s -> a -> s) -> Lens' s a @EncodePanda, paul.szulc@gmail.com 128
  • 129. _organizer :: Lens' Conference Organizer _contact :: Lens' Organizer Contact _address :: Lens' Contact Address _country :: Lens' Address String @EncodePanda, paul.szulc@gmail.com 129
  • 130. _organizer :: Lens' Conference Organizer _organizer = lens getter setter where getter = organizer setter c o = c { organizer = o} @EncodePanda, paul.szulc@gmail.com 130
  • 132. _name :: Lens' Conference String _name = lens getter setter where getter = name setter c n = c { name = n } /../src/TheLens.hs:23:14: error: Ambiguous occurrence ‘name’ It could refer to either the field ‘name’, imported from ‘Example’ at src/TheLens.hs:6:1-24 or the field ‘name’, imported from ‘Example’ at src/TheLens.hs:6:1-24 or the field ‘name’, imported from ‘Example’ at src/TheLens.hs:6:1-24 @EncodePanda, paul.szulc@gmail.com 132
  • 133. _name :: Lens' Conference String _name = lens getter setter where getter = name setter c n = c { name = n } /../src/TheLens.hs:23:14: error: Ambiguous occurrence ‘name’ It could refer to either the field ‘name’, imported from ‘Example’ at src/TheLens.hs:6:1-24 or the field ‘name’, imported from ‘Example’ at src/TheLens.hs:6:1-24 or the field ‘name’, imported from ‘Example’ at src/TheLens.hs:6:1-24 @EncodePanda, paul.szulc@gmail.com 133
  • 134. _name :: Lens' Conference String _name = lens getter setter where getter (Conference n _ _) = n setter (Conference _ o s) n = Conference n o s @EncodePanda, paul.szulc@gmail.com 134
  • 135. _organizer :: Lens' Conference Organizer _contact :: Lens' Organizer Contact _address :: Lens' Contact Address _country :: Lens' Address String @EncodePanda, paul.szulc@gmail.com 135
  • 136. _organizer :: Lens' Conference Organizer _organizer = lens getter setter where getter = organizer setter c o = c { organizer = o} _name :: Lens' Conference String _name = lens getter setter where getter (Conference n _ _) = n setter (Conference _ o s) n = Conference n o s _contact :: Lens' Organizer Contact _contact = lens contact (o -> c -> o { contact = c}) _address :: Lens' Contact Address _address = lens address (c -> a -> c { address = a}) _country :: Lens' Address String _country = lens country (a -> c -> a { country = c}) @EncodePanda, paul.szulc@gmail.com 136
  • 137. Approach #1 - TemplateHaskell @EncodePanda, paul.szulc@gmail.com 137
  • 139. {-# LANGUAGE TemplateHaskell #-} module TheLens where data Ticket = Ticket { _event :: String , _price :: Int } deriving Show makeLenses ''Ticket λ> ticket = Ticket "Haskell Love" 0 λ> ticket ^. price 0 λ> ticket & event .~ "Haskell Love 2020" Ticket {_event = "Haskell Love 2020", _price = 0} @EncodePanda, paul.szulc@gmail.com 139
  • 140. {-# LANGUAGE TemplateHaskell #-} module TheLens where data Ticket = Ticket { _event :: String , _price :: Int } deriving Show makeLenses ''Ticket λ> ticket = Ticket "Haskell Love" 0 λ> ticket ^. price 0 λ> ticket & event .~ "Haskell Love 2020" Ticket {_event = "Haskell Love 2020", _price = 0} @EncodePanda, paul.szulc@gmail.com 140
  • 141. {-# LANGUAGE TemplateHaskell #-} module TheLens where data Ticket = Ticket { _event :: String , _price :: Int } deriving Show makeLenses ''Ticket λ> ticket = Ticket "Haskell Love" 0 λ> ticket ^. price 0 λ> ticket & event .~ "Haskell Love 2020" Ticket {_event = "Haskell Love 2020", _price = 0} @EncodePanda, paul.szulc@gmail.com 141
  • 142. {-# LANGUAGE TemplateHaskell #-} module TheLens where data Ticket = Ticket { _event :: String , _price :: Int } deriving Show makeLenses ''Ticket λ> ticket = Ticket "Haskell Love" 0 λ> ticket ^. price 0 λ> ticket & event .~ "Haskell Love 2020" Ticket {_event = "Haskell Love 2020", _price = 0} @EncodePanda, paul.szulc@gmail.com 142
  • 143. {-# LANGUAGE TemplateHaskell #-} module TheLens where data Ticket = Ticket { _event :: String , _price :: Int } deriving Show makeLenses ''Ticket λ> ticket = Ticket "Haskell Love" 0 λ> ticket ^. price 0 λ> ticket & event .~ "Haskell Love 2020" Ticket {_event = "Haskell Love 2020", _price = 0} @EncodePanda, paul.szulc@gmail.com 143
  • 144. {-# LANGUAGE TemplateHaskell #-} module TheLens where data Ticket = Ticket { _event :: String , _price :: Int } deriving Show makeLenses ''Ticket λ> ticket = Ticket "Haskell Love" 0 λ> ticket ^. price 0 λ> ticket & event .~ "Haskell Love 2020" Ticket {_event = "Haskell Love 2020", _price = 0} @EncodePanda, paul.szulc@gmail.com 144
  • 146. data Ticket = Ticket { _event :: String , _price :: Int } deriving Show makeLenses ''Ticket data Item = Item { _id :: Int , _price :: Int } makeLenses ''Item /../src/TheLens.hs:39:1: error: Multiple declarations of ‘price’ Declared at: src/TheLens.hs:33:1 src/TheLens.hs:39:1 | 39 | makeLenses ''Item | ^^^^^^^^^^^^^^^^^ @EncodePanda, paul.szulc@gmail.com 146
  • 147. data Ticket = Ticket { _event :: String , _price :: Int } deriving Show makeLenses ''Ticket data Item = Item { _id :: Int , _price :: Int } makeLenses ''Item /../src/TheLens.hs:39:1: error: Multiple declarations of ‘price’ Declared at: src/TheLens.hs:33:1 src/TheLens.hs:39:1 | 39 | makeLenses ''Item | ^^^^^^^^^^^^^^^^^ @EncodePanda, paul.szulc@gmail.com 147
  • 148. data Ticket = Ticket { _event :: String , _price :: Int } deriving Show makeLenses ''Ticket data Item = Item { _id :: Int , _price :: Int } makeLenses ''Item /../src/TheLens.hs:39:1: error: Multiple declarations of ‘price’ Declared at: src/TheLens.hs:33:1 src/TheLens.hs:39:1 | 39 | makeLenses ''Item | ^^^^^^^^^^^^^^^^^ @EncodePanda, paul.szulc@gmail.com 148
  • 149. Approach #2 - GHC.Generics @EncodePanda, paul.szulc@gmail.com 149
  • 151. data Conference = Conference { name :: String , organizer :: Organizer , speakers :: [Speaker] } deriving Show data Organizer = Organizer { name :: Name , contact :: Contact } deriving Show ... @EncodePanda, paul.szulc@gmail.com 151
  • 152. data Conference = Conference { name :: String , organizer :: Organizer , speakers :: [Speaker] } deriving (Generic, Show) data Organizer = Organizer { name :: Name , contact :: Contact } deriving (Generic, Show) ... @EncodePanda, paul.szulc@gmail.com 152
  • 153. λ> haskellLove ^. field @"organizer" . field @"contact" . field @"email" "oli@haskell.love" @EncodePanda, paul.szulc@gmail.com 153
  • 154. λ> haskellLove ^. #organizer . #contact . #email "oli@haskell.love" @EncodePanda, paul.szulc@gmail.com 154
  • 155. data Conference = Conference { name :: String , organizer :: Organizer , speakers :: [Speaker] } deriving (Generic, Show) data Organizer = Organizer { name :: Name , contact :: Contact } deriving (Generic, Show) ... @EncodePanda, paul.szulc@gmail.com 155
  • 156. data Conference = Conference { name :: String , organizer :: Organizer , speakers :: [Speaker] } deriving (Generic, Show) data Organizer = Organizer { name :: Name , contact :: Contact } deriving (Generic, Show) ... @EncodePanda, paul.szulc@gmail.com 156
  • 157. λ> haskellLove ^. #name "Haskell.Love" λ> haskellLove ^. #organizer . #name Name {firstName = "Oli", lastName = "Makhasoeva"} @EncodePanda, paul.szulc@gmail.com 157
  • 158. Lens' s a @EncodePanda, paul.szulc@gmail.com 158
  • 159. Lens s t a b @EncodePanda, paul.szulc@gmail.com 159
  • 160. "Shitty records." - Someone on reddit @EncodePanda, paul.szulc@gmail.com 160
  • 162. Lens s t a b @EncodePanda, paul.szulc@gmail.com 162
  • 163. Lens s t a b λ> :t lens lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b type Lens' s a = Lens s s a a @EncodePanda, paul.szulc@gmail.com 163
  • 164. Lens s t a b λ> :t lens lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b type Lens' s a = Lens s s a a @EncodePanda, paul.szulc@gmail.com 164
  • 165. first :: Lens' (a, b) a first = lens getter setter where getter (a, _) = a setter (_, b) a = (a, b) λ> (10, 20) ^. first 10 λ> (10, 20) & first .~ 30 (30,20) λ> (10, 20) & first .~ "hello" <interactive>:25:2: error: • Could not deduce (Num [Char]) arising from the literal ‘10’ from the context: Num b @EncodePanda, paul.szulc@gmail.com 165
  • 166. first :: Lens' (a, b) a first = lens getter setter where getter (a, _) = a setter (_, b) a = (a, b) λ> (10, 20) ^. first 10 λ> (10, 20) & first .~ 30 (30,20) λ> (10, 20) & first .~ "hello" <interactive>:25:2: error: • Could not deduce (Num [Char]) arising from the literal ‘10’ from the context: Num b @EncodePanda, paul.szulc@gmail.com 166
  • 167. first :: Lens' (a, b) a first = lens getter setter where getter (a, _) = a setter (_, b) a = (a, b) λ> (10, 20) ^. first 10 λ> (10, 20) & first .~ 30 (30,20) λ> (10, 20) & first .~ "hello" <interactive>:25:2: error: • Could not deduce (Num [Char]) arising from the literal ‘10’ from the context: Num b @EncodePanda, paul.szulc@gmail.com 167
  • 168. first :: Lens' (a, b) a first = lens getter setter where getter (a, _) = a setter (_, b) a = (a, b) λ> (10, 20) ^. first 10 λ> (10, 20) & first .~ 30 (30,20) λ> (10, 20) & first .~ "hello" <interactive>:25:2: error: • Could not deduce (Num [Char]) arising from the literal ‘10’ from the context: Num b @EncodePanda, paul.szulc@gmail.com 168
  • 169. first :: Lens (a, c) (b, c) a b first = lens getter setter where getter (a, _) = a setter (_, b) c = (c, b) λ> (10, 20) ^. first 10 λ> (10, 20) & first .~ 30 (30,20) λ> (10, 20) & first .~ "hello" ("hello",20) @EncodePanda, paul.szulc@gmail.com 169
  • 170. first :: Lens (a, c) (b, c) a b first = lens getter setter where getter (a, _) = a setter (_, b) c = (c, b) λ> (10, 20) ^. first 10 λ> (10, 20) & first .~ 30 (30,20) λ> (10, 20) & first .~ "hello" ("hello",20) @EncodePanda, paul.szulc@gmail.com 170
  • 171. data Truck a = Truck { name :: String , cargo :: a } deriving (Show, Generic) data Fruit = Apple | Orange deriving Show data Electronic = Computer | Phone deriving Show λ> truck = Truck "Tom" Apple truck :: Truck Fruit λ> otherTruck = truck & #cargo .~ Phone otherTruck :: Truck Electronic @EncodePanda, paul.szulc@gmail.com 171
  • 172. data Truck a = Truck { name :: String , cargo :: a } deriving (Show, Generic) data Fruit = Apple | Orange deriving Show data Electronic = Computer | Phone deriving Show λ> truck = Truck "Tom" Apple truck :: Truck Fruit λ> otherTruck = truck & #cargo .~ Phone otherTruck :: Truck Electronic @EncodePanda, paul.szulc@gmail.com 172
  • 173. data Truck a = Truck { name :: String , cargo :: a } deriving (Show, Generic) data Fruit = Apple | Orange deriving Show data Electronic = Computer | Phone deriving Show λ> truck = Truck "Tom" Apple truck :: Truck Fruit λ> otherTruck = truck & #cargo .~ Phone otherTruck :: Truck Electronic @EncodePanda, paul.szulc@gmail.com 173
  • 174. data Truck a = Truck { name :: String , cargo :: a } deriving (Show, Generic) data Fruit = Apple | Orange deriving Show data Electronic = Computer | Phone deriving Show λ> truck = Truck "Tom" Apple truck :: Truck Fruit λ> otherTruck = truck & #cargo .~ Phone otherTruck :: Truck Electronic @EncodePanda, paul.szulc@gmail.com 174
  • 175. λ> truck = Truck "tom" [Apple, Orange, Apple] λ> truck & #cargo .~ (truck ^. #cargo <> [Orange, Apple]) Truck { name = "tom" , cargo = [Apple,Orange,Apple,Orange,Apple]} λ> truck & #cargo %~ (fruits -> fruits <> [Orange, Apple]) Truck { name = "tom" , cargo = [Apple,Orange,Apple,Orange,Apple]} λ> truck & #cargo <>~ [Orange, Apple] Truck { name = "tom" , cargo = [Apple,Orange,Apple,Orange,Apple]} @EncodePanda, paul.szulc@gmail.com 175
  • 176. λ> truck = Truck "tom" [Apple, Orange, Apple] λ> truck & #cargo .~ (truck ^. #cargo <> [Orange, Apple]) Truck { name = "tom" , cargo = [Apple,Orange,Apple,Orange,Apple]} λ> truck & #cargo %~ (fruits -> fruits <> [Orange, Apple]) Truck { name = "tom" , cargo = [Apple,Orange,Apple,Orange,Apple]} λ> truck & #cargo <>~ [Orange, Apple] Truck { name = "tom" , cargo = [Apple,Orange,Apple,Orange,Apple]} @EncodePanda, paul.szulc@gmail.com 176
  • 177. λ> truck = Truck "tom" [Apple, Orange, Apple] λ> truck & #cargo .~ (truck ^. #cargo <> [Orange, Apple]) Truck { name = "tom" , cargo = [Apple,Orange,Apple,Orange,Apple]} λ> truck & #cargo %~ (fruits -> fruits <> [Orange, Apple]) Truck { name = "tom" , cargo = [Apple,Orange,Apple,Orange,Apple]} λ> truck & #cargo <>~ [Orange, Apple] Truck { name = "tom" , cargo = [Apple,Orange,Apple,Orange,Apple]} @EncodePanda, paul.szulc@gmail.com 177
  • 178. λ> truck = Truck "tom" [Apple, Orange, Apple] λ> truck & #cargo .~ (truck ^. #cargo <> [Orange, Apple]) Truck { name = "tom" , cargo = [Apple,Orange,Apple,Orange,Apple]} λ> truck & #cargo %~ (fruits -> fruits <> [Orange, Apple]) Truck { name = "tom" , cargo = [Apple,Orange,Apple,Orange,Apple]} λ> truck & #cargo <>~ [Orange, Apple] Truck { name = "tom" , cargo = [Apple,Orange,Apple,Orange,Apple]} @EncodePanda, paul.szulc@gmail.com 178
  • 179. λ> truck = Truck "tom" [Apple, Orange, Apple] λ> truck & #cargo %~ length & #cargo +~ 1 Truck {name = "tom", cargo = 4} λ> truck & #cargo %~ length & #cargo *~ 10 Truck {name = "tom", cargo = 30} @EncodePanda, paul.szulc@gmail.com 179
  • 181. Traversal s t a b @EncodePanda, paul.szulc@gmail.com 181
  • 182. λ> haskellLove ^. #speakers [ Speaker { name = Name {firstName = "Pawel", lastName = "Szulc"} , slidesReady = False} , Speaker { name = Name {firstName = "Marcin", lastName = "Rzeznicki"} , slidesReady = True} ] λ> haskellLove ^.. #speakers . traversed . #name . #lastName ["Szulc","Rzeznicki"] λ> haskellLove ^. #speakers . traversed . #name . #lastName "SzulcRzeznicki" @EncodePanda, paul.szulc@gmail.com 182
  • 183. λ> haskellLove ^. #speakers [ Speaker { name = Name {firstName = "Pawel", lastName = "Szulc"} , slidesReady = False} , Speaker { name = Name {firstName = "Marcin", lastName = "Rzeznicki"} , slidesReady = True} ] λ> haskellLove ^.. #speakers . traversed . #name . #lastName ["Szulc","Rzeznicki"] λ> haskellLove ^. #speakers . traversed . #name . #lastName "SzulcRzeznicki" @EncodePanda, paul.szulc@gmail.com 183
  • 184. λ> haskellLove ^. #speakers [ Speaker { name = Name {firstName = "Pawel", lastName = "Szulc"} , slidesReady = False} , Speaker { name = Name {firstName = "Marcin", lastName = "Rzeznicki"} , slidesReady = True} ] λ> haskellLove ^.. #speakers . traversed . #name . #lastName ["Szulc","Rzeznicki"] λ> haskellLove ^. #speakers . traversed . #name . #lastName "SzulcRzeznicki" @EncodePanda, paul.szulc@gmail.com 184
  • 185. Anyone ready? anyOf :: Traversal s t a b -> (a -> Bool) -> s -> Bool λ> anyOf (#speakers . traversed . #slidesReady) id haskellLove True @EncodePanda, paul.szulc@gmail.com 185
  • 186. Anyone ready? anyOf :: Traversal s t a b -> (a -> Bool) -> s -> Bool λ> anyOf (#speakers . traversed . #slidesReady) id haskellLove True @EncodePanda, paul.szulc@gmail.com 186
  • 187. λ> haskellLove & #speakers %~ (fmap (set #slidesReady False)) Conference { name = "Haskell.Love" , organizer = Organizer {..} , speakers = [ Speaker { name = Name { firstName = "Pawel" , lastName = "Szulc"} , slidesReady = False} , Speaker { name = Name { firstName = "Marcin" , lastName = "Rzeznicki"} , slidesReady = False} ] } @EncodePanda, paul.szulc@gmail.com 187
  • 188. λ> haskellLove & #speakers . traversed . #slidesReady .~ False Conference { name = "Haskell.Love" , organizer = Organizer {..} , speakers = [ Speaker { name = Name { firstName = "Pawel" , lastName = "Szulc"} , slidesReady = False} , Speaker { name = Name { firstName = "Marcin" , lastName = "Rzeznicki"} , slidesReady = False} ] } @EncodePanda, paul.szulc@gmail.com 188
  • 189. Reveal the magic @EncodePanda, paul.szulc@gmail.com 189
  • 190. Exhibit A: Lens s t a b 1. How to encode getter and setter in one type? 2. How to compose via "dot"? @EncodePanda, paul.szulc@gmail.com 190
  • 191. data Lenz s t a b = Lenz { view :: s -> a , set :: s -> b -> t } over :: Lenz s t a b -> (a -> b) -> s -> t over lenz func s = let old = view lenz s new = func old in set lenz s new type Lenz' s a = Lenz s s a a @EncodePanda, paul.szulc@gmail.com 191
  • 192. data Lenz s t a b = Lenz { view :: s -> a , set :: s -> b -> t } over :: Lenz s t a b -> (a -> b) -> s -> t over lenz func s = let old = view lenz s new = func old in set lenz s new type Lenz' s a = Lenz s s a a @EncodePanda, paul.szulc@gmail.com 192
  • 193. data Lenz s t a b = Lenz { view :: s -> a , set :: s -> b -> t } over :: Lenz s t a b -> (a -> b) -> s -> t over lenz func s = let old = view lenz s new = func old in set lenz s new type Lenz' s a = Lenz s s a a @EncodePanda, paul.szulc@gmail.com 193
  • 194. But that "dot" though... @EncodePanda, paul.szulc@gmail.com 194
  • 195. data Lenz s t a b = Lenz { view :: s -> a , set :: s -> b -> t } @EncodePanda, paul.szulc@gmail.com 195
  • 196. data Lenz s t a b = Lenz { lenz :: s -> (a, b -> t) } @EncodePanda, paul.szulc@gmail.com 196
  • 197. type Lenz s t a b = s -> (a, b -> t) @EncodePanda, paul.szulc@gmail.com 197
  • 198. type Lenz s t a b = s -> (a, b -> t) But this will not compose with itself. @EncodePanda, paul.szulc@gmail.com 198
  • 199. type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t @EncodePanda, paul.szulc@gmail.com 199
  • 201. type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t @EncodePanda, paul.szulc@gmail.com 201
  • 202. type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t organizer :: Lens' Conference Organizer contact :: Lens' Organizer Contact forall f. Functor f => (Organizer -> f Organizer) -> (Conference -> f Conference) forall f. Functor f => (Contact-> f Contact) -> (Organizer -> Organizer) @EncodePanda, paul.szulc@gmail.com 202
  • 203. type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t organizer :: Lens' Conference Organizer contact :: Lens' Organizer Contact forall f. Functor f => (Organizer -> f Organizer) -> (Conference -> f Conference) forall f. Functor f => (Contact-> f Contact) -> (Organizer -> Organizer) @EncodePanda, paul.szulc@gmail.com 203
  • 204. type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t organizer :: Lens' Conference Organizer contact :: Lens' Organizer Contact forall f. Functor f => (Organizer -> f Organizer) -> (Conference -> f Conference) forall f. Functor f => (Contact-> f Contact) -> (Organizer -> Organizer) @EncodePanda, paul.szulc@gmail.com 204
  • 205. type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t organizer :: Lens' Conference Organizer contact :: Lens' Organizer Contact forall f. Functor f => (Organizer -> f Organizer) -> (Conference -> f Conference) forall f. Functor f => (Contact-> f Contact) -> (Organizer -> Organizer) :t organizer . contact forall f. Functor f => (Contact-> f Contact) -> (Conference-> Conference) @EncodePanda, paul.szulc@gmail.com 205
  • 206. type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t @EncodePanda, paul.szulc@gmail.com 206
  • 207. type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t over :: Lens s t a b -> (a -> b) -> s -> t set :: Lens s t a b -> b -> s -> t view :: Lens s t a b -> s -> a @EncodePanda, paul.szulc@gmail.com 207
  • 208. type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t over :: Lens s t a b -> (a -> b) -> s -> t set :: Lens s t a b -> b -> s -> t view :: Lens s t a b -> s -> a @EncodePanda, paul.szulc@gmail.com 208
  • 209. type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t over :: Lens s t a b -> (a -> b) -> s -> t set :: Lens s t a b -> b -> s -> t view :: Lens s t a b -> s -> a @EncodePanda, paul.szulc@gmail.com 209
  • 210. type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t over :: Lens s t a b -> (a -> b) -> s -> t set :: Lens s t a b -> b -> s -> t view :: Lens s t a b -> s -> a @EncodePanda, paul.szulc@gmail.com 210
  • 211. type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t over :: Lens s t a b -> (a -> b) -> s -> t over l func s = @EncodePanda, paul.szulc@gmail.com 211
  • 212. type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t over :: Lens s t a b -> (a -> b) -> s -> t over l func s = l (Identity . func) s @EncodePanda, paul.szulc@gmail.com 212
  • 213. type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t over :: Lens s t a b -> (a -> b) -> s -> t over l func s = runIdentity . l (Identity . func) $ s @EncodePanda, paul.szulc@gmail.com 213
  • 214. type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t over :: Lens s t a b -> (a -> b) -> s -> t set :: Lens s t a b -> b -> s -> t set l b s = over l (const b) s @EncodePanda, paul.szulc@gmail.com 214
  • 215. type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t view :: Lens s t a b -> s -> a @EncodePanda, paul.szulc@gmail.com 215
  • 216. newtype Const a b = Const { getConst :: a } instance Functor (Const m) where fmap _ (Const v) = Const v @EncodePanda, paul.szulc@gmail.com 216
  • 217. type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t view :: Lens s t a b -> s -> a @EncodePanda, paul.szulc@gmail.com 217
  • 218. type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t view :: Lens s t a b -> s -> a view l s = l Const s @EncodePanda, paul.szulc@gmail.com 218
  • 219. type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t view :: Lens s t a b -> s -> a view l s = getConst . l Const $ s @EncodePanda, paul.szulc@gmail.com 219
  • 220. view :: Lens s t a b -> s -> a view l s = getConst . l Const $ s @EncodePanda, paul.szulc@gmail.com 220
  • 225. @EncodePanda (Pawel Szulc) @EncodePanda, paul.szulc@gmail.com 225