Upgrade to Pro — share decks privately, control downloads, hide ads and more …

lens: from the ground up (in clojure)

Mark Hibberd
February 27, 2014

lens: from the ground up (in clojure)

A look at functional lenses in general, and how they would work in an uni-typed language like clojure. This talk walks through why lenses are useful, how to encode lenses, and builds up a small lens library in clojure.

The demonstration code is available at https://github.com/markhibberd/lens-talk-clojure (see lens/script.org src/*.clj)

This talk is derived from a more in-depth presentation into the haskell "lens" library: https://speakerdeck.com/markhibberd/lens-from-the-ground-up

Mark Hibberd

February 27, 2014
Tweet

More Decks by Mark Hibberd

Other Decks in Programming

Transcript

  1. lens
    @markhibberd
    from the ground up

    View full-size slide

  2. (defrecord Address [street city postcode])
    !
    (defrecord Person [name age address])
    !
    (defrecord User [uid username identity password])
    records

    View full-size slide

  3. (def home (->Address "road" "place" 4000))
    !
    (def mark (->Person "mark" 99 home))
    !
    (def user (->User 3365 "mth" mark
    "6aefd2842be62cd470709b27aedc7db7"))
    records
    (defrecord Address [street city postcode])
    !
    (defrecord Person [name age address])
    !
    (defrecord User [uid username identity password])

    View full-size slide

  4. !
    (def updated (update-in user [:identity]
    (fn [person] (update-in person [:address]
    (fn [address] (update-in address [:postcode]
    #(+ 4 %)))))))
    records
    (defrecord Address [street city postcode])
    !
    (defrecord Person [name age address])
    !
    (defrecord User [uid username identity password])

    View full-size slide

  5. !
    (update-in user
    [:identity :address :postcode] #(+ 2 %))
    records
    (defrecord Address [street city postcode])
    !
    (defrecord Person [name age address])
    !
    (defrecord User [uid username identity password])
    Can we do better?!

    View full-size slide

  6. the ground up

    View full-size slide

  7. ; get :: a -> b
    ; set :: a -> b -> a
    !
    (defrecord Lens
    [getter setter])
    !
    (defn lget [lens a]
    ((:getter lens) a))
    !
    (defn lset [lens a b]
    ((:setter lens) a b))
    intuitions

    View full-size slide

  8. ; get :: a -> b
    ; set :: a -> b -> a
    !
    (defrecord Lens
    [getter setter])
    !
    (defn lget [lens a]
    ((:getter lens) a))
    !
    (defn lset [lens a b]
    ((:setter lens) a b))
    set-get ==>
    (get l (set l a b)) == b
    !
    get-set ==>
    (set l a (get l a)) == a
    !
    set-set ==>
    (set l (set l b a) c) ==
    (set l a c)
    intuitions
    Pierce’s laws!

    View full-size slide

  9. but…
    !
    (defn lmodify [lens a func]
    ((:setter lens) a (func ((:getter lens) a))))
    !
    (defn lcompose [lens-a-b lens-b-c]
    (->Lens
    (fn [a] (lget lens-b-c (lget lens-a-b a)))
    (fn [a c] (lset lens-a-b a
    (lset lens-b-c
    (lget lens-a-b a) c)))))

    View full-size slide

  10. but…
    efficiency matters
    composition matters
    partiality matters
    elegance matters

    View full-size slide

  11. !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    back to the drawing board
    haskell

    View full-size slide

  12. !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    back to the drawing board
    haskell
    !
    !
    clojure

    View full-size slide

  13. !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    back to the drawing board
    !
    (deffn set [lens a b] (“can we?”))
    !
    (deffn get [lens a] (“can we?”))

    View full-size slide

  14. !
    (defprotocol Functor
    (fmap [functor f]
    "fmap :: f a -> (a -> b) -> f b"))
    aside: what is a functor

    View full-size slide

  15. !
    (defprotocol Functor
    (fmap [functor f]
    "fmap :: f a -> (a -> b) -> f b"))
    !
    (defrecord List [wrapped]
    Functor (fmap [functor f]
    (List.
    (map f (:wrapped functor)))))
    aside: what is a functor

    View full-size slide

  16. !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    back to the drawing board
    (defrecord Id [runId]
    Functor
    (fmap [functor f]
    (Id. (f (:runId functor)))))
    !
    (deffn -set [lens a] (???))
    !
    !
    !

    View full-size slide

  17. !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    back to the drawing board
    (defrecord Id [runId]
    Functor
    (fmap [functor f]
    (Id. (f (:runId functor)))))
    !
    (deffn -set [lens a b]
    (let [b-ib (fn [bb] (->Id b)) -- b -> Id b
    ]
    (“???”)))

    View full-size slide

  18. !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    back to the drawing board
    (defrecord Id [runId]
    Functor
    (fmap [functor f]
    (Id. (f (:runId functor)))))
    !
    (deffn -set [lens a b]
    (let [b-ib (fn [bb] (->Id b)) -- b -> Id b
    a-ia (lens b-ib) -- a -> Id a
    ]
    (“???”)))

    View full-size slide

  19. !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    back to the drawing board
    (defrecord Id [runId]
    Functor
    (fmap [functor f]
    (Id. (f (:runId functor)))))
    !
    (deffn -set [lens a b]
    (let [b-ib (fn [bb] (->Id b)) -- b -> Id b
    a-ia (lens b-ib) -- a -> Id a
    ia (a-ia a)] -- Id a
    (“???”)))

    View full-size slide

  20. !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    back to the drawing board
    (defrecord Id [runId]
    Functor
    (fmap [functor f]
    (Id. (f (:runId functor)))))
    !
    (deffn -set [lens a b]
    (let [b-ib (fn [bb] (->Id b)) -- b -> Id b
    a-ia (lens b-ib) -- a -> Id a
    ia (a-ia a)] -- Id a
    (:runId ia))) -- a

    View full-size slide

  21. !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    back to the drawing board
    (defrecord ??? [???]
    Functor
    (fmap [functor f] ???))
    !
    (deffn -get [lens a] (???))
    !
    !
    !
    !

    View full-size slide

  22. !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    back to the drawing board
    (defrecord Const [runConst]
    Functor
    (fmap [functor f]
    (Const. (:runConst functor))))
    !
    (deffn -get [lens a] (???)
    !
    !
    !

    View full-size slide

  23. !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    back to the drawing board
    (defrecord Const [runConst]
    Functor
    (fmap [functor f]
    (Const. (:runConst functor))))
    !
    (deffn -get [lens a]
    (let [b-cb (fn [b] (->Const b)) -- b -> Const b
    ]
    (???)))

    View full-size slide

  24. !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    back to the drawing board
    (defrecord Const [runConst]
    Functor
    (fmap [functor f]
    (Const. (:runConst functor))))
    !
    (deffn -get [lens a]
    (let [b-cb (fn [b] (->Const b)) -- b -> Const b
    a-ca (lens b-cb) -- a -> Const a
    ]
    (???)))

    View full-size slide

  25. !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    back to the drawing board
    (defrecord Const [runConst]
    Functor
    (fmap [functor f]
    (Const. (:runConst functor))))
    !
    (deffn -get [lens a]
    (let [b-cb (fn [b] (->Const b)) -- b -> Const b
    a-ca (lens b-cb) -- a -> Const a
    ca (a-ca a)] -- Const a
    (???)))

    View full-size slide

  26. !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    back to the drawing board
    (defrecord Const [runConst]
    Functor
    (fmap [functor f]
    (Const. (:runConst functor))))
    !
    (deffn -get [lens a]
    (let [b-cb (fn [b] (->Const b)) -- b -> Const b
    a-ca (lens b-cb) -- a -> Const a
    ca (a-ca a)] -- Const a
    (:runConst ca))) -- a

    View full-size slide

  27. !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    back to the drawing board
    (defrecord Id [runId]
    Functor
    (fmap [functor f]
    (Id. (f (:runId functor)))))
    !
    (deffn -modify [lens a f]
    (let [b-ib (fn [bb] (->Id (f bb))) -- b -> Id b
    a-ia (lens b-ib) -- a -> Id a
    ia (a-ia a)] -- Id a
    (:runId ia))) -- a

    View full-size slide

  28. functional elegance

    View full-size slide

  29. composition
    !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a

    View full-size slide

  30. function composition
    !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    x :: Lens a b
    y :: Lens b c
    (comp x y) :: Lens a c
    !
    !
    !
    !
    !

    View full-size slide

  31. function composition
    !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    x :: Lens a b :: (b -> f b) -> (a -> f a)
    y :: Lens b c
    (comp x y) :: Lens a c
    !
    !
    !
    !
    !

    View full-size slide

  32. function composition
    !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    x :: Lens a b :: (b -> f b) -> (a -> f a)
    y :: Lens b c :: (c -> f c) -> (b -> f b)
    (comp x y) :: Lens a c
    !
    !
    !
    !
    !

    View full-size slide

  33. function composition
    !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    x :: Lens a b :: (b -> f b) -> (a -> f a)
    y :: Lens b c :: (c -> f c) -> (b -> f b)
    (comp x y) :: Lens a c :: (c -> f c) -> (a -> f a)
    !
    !
    !
    !
    !

    View full-size slide

  34. function composition
    !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    x :: Lens a b :: (b -> f b) -> (a -> f a)
    y :: Lens b c :: (c -> f c) -> (b -> f b)
    (comp x y) :: Lens a c :: (c -> f c) -> (a -> f a)
    !
    (comp) :: (i -> j) -> (h -> i) -> h -> j
    !
    !
    !

    View full-size slide

  35. function composition
    !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    x :: Lens a b :: (b -> f b) -> (a -> f a)
    y :: Lens b c :: (c -> f c) -> (b -> f b)
    (comp x y) :: Lens a c :: (c -> f c) -> (a -> f a)
    !
    (comp) :: (i -> j) -> (h -> i) -> h -> j
    !
    (defn comp [f g]
    (fn [a] (f (g a))))

    View full-size slide

  36. function composition
    !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    x :: Lens a b :: (b -> f b) -> (a -> f a)
    y :: Lens b c :: (c -> f c) -> (b -> f b)
    (comp x y) :: Lens a c :: (c -> f c) -> (a -> f a)
    !
    (comp) :: (i -> j) -> (h -> i) -> h -> j
    !
    x :: i -> j
    !

    View full-size slide

  37. function composition
    !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    x :: Lens a b :: (b -> f b) -> (a -> f a)
    y :: Lens b c :: (c -> f c) -> (b -> f b)
    (comp x y) :: Lens a c :: (c -> f c) -> (a -> f a)
    !
    (comp) :: (i -> j) -> (h -> i) -> h -> j
    !
    x :: i -> j, y :: h -> i
    !

    View full-size slide

  38. function composition
    !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    x :: Lens a b :: (b -> f b) -> (a -> f a)
    y :: Lens b c :: (c -> f c) -> (b -> f b)
    (comp x y) :: Lens a c :: (c -> f c) -> (a -> f a)
    !
    (comp) :: (i -> j) -> (h -> i) -> h -> j
    !
    x :: i -> j, y :: h -> i
    i :: b -> f b

    View full-size slide

  39. function composition
    !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    x :: Lens a b :: (b -> f b) -> (a -> f a)
    y :: Lens b c :: (c -> f c) -> (b -> f b)
    (comp x y) :: Lens a c :: (c -> f c) -> (a -> f a)
    !
    (comp) :: (i -> j) -> (h -> i) -> h -> j
    !
    x :: i -> j, y :: h -> i
    i :: b -> f b, j :: a -> f a

    View full-size slide

  40. function composition
    !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    x :: Lens a b :: (b -> f b) -> (a -> f a)
    y :: Lens b c :: (c -> f c) -> (b -> f b)
    (comp x y) :: Lens a c :: (c -> f c) -> (a -> f a)
    !
    (comp) :: (i -> j) -> (h -> i) -> h -> j
    !
    x :: i -> j, y :: h -> i
    i :: b -> f b, j :: a -> f a, h :: c -> f c

    View full-size slide

  41. function composition
    !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    x :: Lens a b :: (b -> f b) -> (a -> f a)
    y :: Lens b c :: (c -> f c) -> (b -> f b)
    (comp x y) :: Lens a c :: (c -> f c) -> (a -> f a)
    !
    (comp) :: (i -> j) -> (h -> i) -> h -> j
    !
    x :: i -> j, y :: h -> i
    i :: b -> f b, j :: a -> f a, h :: c -> f c
    (comp x y) :: h -> j

    View full-size slide

  42. function composition
    !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    x :: Lens a b :: (b -> f b) -> (a -> f a)
    y :: Lens b c :: (c -> f c) -> (b -> f b)
    (comp x y) :: Lens a c :: (c -> f c) -> (a -> f a)
    !
    (comp) :: (i -> j) -> (h -> i) -> h -> j
    !
    x :: i -> j, y :: h -> i
    i :: b -> f b, j :: a -> f a, h :: c -> f c
    (comp x y) :: h -> j :: (c -> f c) -> (a -> f a)

    View full-size slide

  43. function composition
    !
    type Lens’ a b =
    forall f. Functor f => (b -> f b) -> a -> f a
    x :: Lens a b :: (b -> f b) -> (a -> f a)
    y :: Lens b c :: (c -> f c) -> (b -> f b)
    (comp x y) :: Lens a c :: (c -> f c) -> (a -> f a)
    !
    (comp) :: (i -> j) -> (h -> i) -> h -> j
    !
    x :: i -> j, y :: h -> i
    i :: b -> f b, j :: a -> f a, h :: c -> f c
    (comp x y) :: h -> j :: (c -> f c) -> (a -> f a)

    View full-size slide

  44. type Getter s a =
    forall r. (a -> Const r a) -> s -> Const r s
    !
    type Setter s t a b =
    (a -> Identity b) -> s -> Identity t
    mirrored lenses

    View full-size slide

  45. multiplate
    type Traversal a a’ b b’ =
    forall f. Applicative f =>
    (b -> f b’) -> a -> f a’

    View full-size slide

  46. type Prism s t a b =
    forall p f. (Choice p, Applicative f) =>
    p a (f b) -> p s (f t)
    prisms / co-lens

    View full-size slide

  47. references
    • Pierce’s original work on lenses / bidirectional programming:
    • http://www.cis.upenn.edu/~bcpierce/papers/index.shtml#Lenses
    • van Laarhoven’s original write-up:
    • http://www.twanvl.nl/blog/haskell/cps-functional-references
    • O’Connor’s coining of the term “van Laarhoven lens” and the insight into polymorphic update:
    • http://r6.ca/blog/20120623T104901Z.html
    • O’Connor’s multiplate paper (a.k.a. traversals):
    • http://arxiv.org/pdf/1103.2841v2.pdf
    • Kmett’s expanding on mirrored lens insights (a.k.a. read-only / write only):
    • http://comonad.com/reader/2012/mirrored-lenses/
    • lens website, lots of links and video:
    • http://lens.github.io/
    • git repo:
    • https://github.com/ekmett/lens
    • hackage:
    • http://hackage.haskell.org/package/lens
    • this talk in haskell:
    • https://speakerdeck.com/markhibberd/lens-from-the-ground-up

    View full-size slide

  48. fin
    @markhibberd

    View full-size slide