lens: from the ground up (in clojure)

42d9867a0fee0fa6de6534e9df0f1e9b?s=47 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

42d9867a0fee0fa6de6534e9df0f1e9b?s=128

Mark Hibberd

February 27, 2014
Tweet

Transcript

  1. lens @markhibberd from the ground up

  2. motivations

  3. (defrecord Address [street city postcode]) ! (defrecord Person [name age

    address]) ! (defrecord User [uid username identity password]) records
  4. (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])
  5. ! (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])
  6. ! (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?!
  7. the ground up

  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)) intuitions
  9. ; 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!
  10. 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)))))
  11. but… efficiency matters composition matters partiality matters elegance matters

  12. ! type Lens’ a b = forall f. Functor f

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

    => (b -> f b) -> a -> f a back to the drawing board haskell ! ! clojure
  14. ! 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?”))
  15. ! (defprotocol Functor (fmap [functor f] "fmap :: f a

    -> (a -> b) -> f b")) aside: what is a functor
  16. ! (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
  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] (???)) ! ! !
  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 ] (“???”)))
  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 ] (“???”)))
  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 (“???”)))
  21. ! 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
  22. ! 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] (???)) ! ! ! !
  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] (???) ! ! !
  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 ] (???)))
  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 ] (???)))
  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 (???)))
  27. ! 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
  28. ! 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
  29. functional elegance

  30. composition ! type Lens’ a b = forall f. Functor

    f => (b -> f b) -> a -> f a
  31. 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 ! ! ! ! !
  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 (comp x y) :: Lens a c ! ! ! ! !
  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 ! ! ! ! !
  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) ! ! ! ! !
  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 ! ! !
  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 ! (defn comp [f g] (fn [a] (f (g a))))
  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 !
  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 !
  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
  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
  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
  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
  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)
  44. 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)
  45. 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
  46. multiplate type Traversal a a’ b b’ = forall f.

    Applicative f => (b -> f b’) -> a -> f a’
  47. type Prism s t a b = forall p f.

    (Choice p, Applicative f) => p a (f b) -> p s (f t) prisms / co-lens
  48. code

  49. 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
  50. fin @markhibberd