lens: from the ground up (at BFPG)

lens: from the ground up (at BFPG)

A look at functional lenses in general, and the haskell "lens" library specifically. This talk walks through why lenses are encoded the way they are, and builds up to demonstrations of the lens library.

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

This is a slightly different version to what was presented at fp-syd.

42d9867a0fee0fa6de6534e9df0f1e9b?s=128

Mark Hibberd

March 17, 2014
Tweet

Transcript

  1. lens @markhibberd from the ground up

  2. motivations

  3. the ground up

  4. data Lens a b = Lens { get :: a

    -> b , set :: b -> a -> a } intuitions
  5. data Lens a b = Lens { get :: a

    -> b , set :: b -> a -> a } intuitions set-get ==> get l (set l b a) == b ! get-set ==> set l (get l a) a == a ! set-set ==> set l c (set l b a) == set l c a Pierce’s laws!
  6. but… modify :: Lens a b -> (b -> b)

    -> a -> a modify l f a = set l (f (get l a)) a ! compose :: Lens a b -> Lens b c -> Lens a c compose l j = Lens (\a -> get j (get l a)) (\c a -> set l (set j c (get l a)) a)
  7. but… modify :: Lens a b -> (b -> b)

    -> a -> a modify l f a = set l (f (get l a)) a ! compose :: Lens a b -> Lens b c -> Lens a c compose l j = Lens (\a -> get j (get l a)) (\c a -> set l (set j c (get l a)) a) efficiency matters!
  8. but… data Wedge a = Wedge { _name :: String,

    _val :: a } ! name :: Lens (Wedge a) String name = Lens _name (\n w -> w { _name = n }) ! value :: Lens (Wedge a) a value = Lens _val (\v w -> w { _val = v })
  9. but… data Wedge a = Wedge { _name :: String,

    _val :: a } ! name :: Lens (Wedge a) String name = Lens _name (\n w -> w { _name = n }) ! value :: Lens (Wedge a) a value = Lens _val (\v w -> w { _val = v }) polymorphic update matters
  10. but… data Safety = Safety { _readOnly :: String }

    ! readOnly :: Lens Safety String readOnly = Lens _readOnly (error "don't do this")
  11. but… read only / write only matters data Safety =

    Safety { _readOnly :: String } ! readOnly :: Lens Safety String readOnly = Lens _readOnly (error "don't do this")
  12. but… (&&&) :: Lens a b -> Lens a c

    -> Lens a (b, c) (&&&) l j = Lens (\a -> (get l a, get j a)) (\(b, c) a -> set j c (set l b a))
  13. but… (&&&) :: Lens a b -> Lens a c

    -> Lens a (b, c) (&&&) l j = Lens (\a -> (get l a, get j a)) (\(b, c) a -> set j c (set l b a)) not a lens
  14. but… (&&&) :: Lens a b -> Lens a c

    -> Lens a (b, c) (&&&) l j = Lens (\a -> (get l a, get j a)) (\(b, c) a -> set j c (set l b a)) composition matters not a lens
  15. but… data OneOf = First String | Second Int !

    first :: Lens OneOf (Maybe String) first = undefined ! first :: Lens OneOf (Maybe String) first = undefined
  16. but… data OneOf = First String | Second Int !

    first :: Lens OneOf (Maybe String) first = undefined ! first :: Lens OneOf (Maybe String) first = undefined not a lens
  17. but… data OneOf = First String | Second Int !

    first :: Lens OneOf (Maybe String) first = undefined ! first :: Lens OneOf (Maybe String) first = undefined not a lens partiality matters
  18. failed experiments data Store s a = Store (s ->

    a) s ! data Lens a b = Lens (a -> Store b a) ! ! ! ! ! ! ! !
  19. failed experiments data Store s a = Store (s ->

    a) s ! data Lens a b = Lens (a -> Store b a) ! ! ! ! ! ! ! ! data Lens a b = Lens { get :: a -> b , set :: b -> a -> a }
  20. failed experiments data Store s a = Store (s ->

    a) s ! data Lens a b = Lens (a -> Store b a) ! ! ! ! ! ! ! ! data Lens a b = Lens { get :: a -> b , set :: a -> b -> a }
  21. failed experiments data Store s a = Store (s ->

    a) s ! data Lens a b = Lens (a -> Store b a) ! ! ! ! ! ! ! ! data Lens a b = Lens { run :: a ->(b ,b -> a) }
  22. data Store s a = Store (s -> a) s

    ! data Lens a b = Lens (a -> Store b a) ! get :: Lens a b -> a -> b get (Lens l) a = case l a of Store _ s -> s ! set :: Lens a b -> b -> a -> a set (Lens l) b a = case l a of Store f _ -> f b failed experiments
  23. data Store s a = Store (s -> a) s

    ! data Lens a b = Lens (a -> Store b a) ! get :: Lens a b -> a -> b get (Lens l) a = case l a of Store _ s -> s ! set :: Lens a b -> b -> a -> a set (Lens l) b a = case l a of Store f _ -> f b polymorphic update matters composition matters failed experiments partiality matters read only / write only matters
  24. ! type Lens’ a b = forall f. Functor f

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

    => (b -> f b) -> a -> f a back to the drawing board ! set :: Lens’ a b -> b -> a -> a set = error “can we?” ! get :: Lens’ a b -> a -> b get = error “can we?”
  26. ! type Lens’ a b = forall f. Functor f

    => (b -> f b) -> a -> f a back to the drawing board newtype Identity a = Identity { runIdentity :: a } ! set :: Lens’ a b -> b -> a -> a set l b a = let x = const $ Identity b -- :: b -> Identity b in undefined ! !
  27. ! type Lens’ a b = forall f. Functor f

    => (b -> f b) -> a -> f a back to the drawing board newtype Identity a = Identity { runIdentity :: a } ! set :: Lens’ a b -> b -> a -> a set l b a = let x = const $ Identity b -- :: b -> Identity b y = l x -- :: a -> Identity a in runIdentity z !
  28. ! type Lens’ a b = forall f. Functor f

    => (b -> f b) -> a -> f a back to the drawing board newtype Identity a = Identity { runIdentity :: a } ! set :: Lens’ a b -> b -> a -> a set l b a = let x = const $ Identity b -- :: b -> Identity b y = l x -- :: a -> Identity a z = y a -- :: Identity a in undefined
  29. ! type Lens’ a b = forall f. Functor f

    => (b -> f b) -> a -> f a back to the drawing board newtype Identity a = Identity { runIdentity :: a } ! set :: Lens’ a b -> b -> a -> a set l b a = let x = const $ Identity b -- :: b -> Identity b y = l x -- :: a -> Identity a z = y a -- :: Identity a in runIdentity z
  30. ! type Lens’ a b = forall f. Functor f

    => (b -> f b) -> a -> f a back to the drawing board newtype Identity a = Identity { runIdentity :: a } ! set :: Lens’ a b -> b -> a -> a set l b a = runIdentity (l (const $ Identity b) a) ! ! !
  31. ! type Lens’ a b = forall f. Functor f

    => (b -> f b) -> a -> f a back to the drawing board newtype Identity a = Identity { runIdentity :: a } ! set :: Lens’ a b -> b -> a -> a set l b a = runIdentity . l (const $ Identity b) $ a ! ! !
  32. ! type Lens’ a b = forall f. Functor f

    => (b -> f b) -> a -> f a back to the drawing board newtype Identity a = Identity { runIdentity :: a } ! set :: Lens’ a b -> b -> a -> a set l b = runIdentity . l (const $ Identity b) ! ! !
  33. ! type Lens’ a b = forall f. Functor f

    => (b -> f b) -> a -> f a back to the drawing board newtype ??? a = ??? ! get :: Lens’ a b -> a -> b get l a = undefined ! ! !
  34. ! type Lens’ a b = forall f. Functor f

    => (b -> f b) -> a -> f a back to the drawing board newtype Const x a = Const x ! get :: Lens’ a b -> a -> b get l a = undefined ! ! !
  35. ! type Lens’ a b = forall f. Functor f

    => (b -> f b) -> a -> f a back to the drawing board newtype Const x a = Const x ! get :: Lens’ a b -> a -> b get l a = undefined ! ! ! ! instance Functor (Const x) where fmap _ = Const . runConst
  36. ! type Lens’ a b = forall f. Functor f

    => (b -> f b) -> a -> f a back to the drawing board newtype Const x a = Const x ! get :: Lens’ a b -> a -> b get l a = let x = Const -- :: b -> Const b b in undefined ! !
  37. ! type Lens’ a b = forall f. Functor f

    => (b -> f b) -> a -> f a back to the drawing board newtype Const x a = Const x ! get :: Lens’ a b -> a -> b get l a = let x = Const -- :: b -> Const b b y = l x -- :: a -> Const b a in undefined !
  38. ! type Lens’ a b = forall f. Functor f

    => (b -> f b) -> a -> f a back to the drawing board newtype Const x a = Const x ! get :: Lens’ a b -> a -> b get l a = let x = Const -- :: b -> Const b b y = l x -- :: a -> Const b a z = y a -- :: Const b a in undefined
  39. ! type Lens’ a b = forall f. Functor f

    => (b -> f b) -> a -> f a back to the drawing board newtype Const x a = Const x ! get :: Lens’ a b -> a -> b get l a = let x = Const -- :: b -> Const b b y = l x -- :: a -> Const b a z = y a -- :: Const b a in runConst z
  40. ! type Lens’ a b = forall f. Functor f

    => (b -> f b) -> a -> f a back to the drawing board newtype Const x a = Const x ! get :: Lens’ a b -> a -> b get l a = runConst (l Const a) ! ! !
  41. ! type Lens’ a b = forall f. Functor f

    => (b -> f b) -> a -> f a back to the drawing board newtype Const x a = Const x ! get :: Lens’ a b -> a -> b get l a = runConst . l Const $ a ! ! !
  42. ! type Lens’ a b = forall f. Functor f

    => (b -> f b) -> a -> f a back to the drawing board newtype Const x a = Const x ! get :: Lens’ a b -> a -> b get l = runConst . l Const ! ! !
  43. ! type Lens’ a b = forall f. Functor f

    => (b -> f b) -> a -> f a back to the drawing board set :: Lens’ a b -> b -> a -> a set l b = runIdentity . l (const $ Identity b) ! get :: Lens’ a b -> a -> b get l = runConst . l Const ! ! ! !
  44. ! type Lens’ a b = forall f. Functor f

    => (b -> f b) -> a -> f a back to the drawing board set :: Lens’ a b -> b -> a -> a set l b = runIdentity . l (const $ Identity b) ! get :: Lens’ a b -> a -> b get l = runConst . l Const ! modify :: Lens’ a b -> (b -> b) -> a modify l f = runIdentity . l (Identity . f) !
  45. functional elegance

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

    f => (b -> f b) -> a -> f a
  47. function composition ! type Lens’ a b = forall f.

    Functor f => (b -> f b) -> a -> f a x :: Lens a b y :: Lens b c x . y :: Lens a c ! ! ! ! !
  48. 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 x . y :: Lens a c ! ! ! ! !
  49. 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) x . y :: Lens a c ! ! ! ! !
  50. 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) x . y :: Lens a c :: (c -> f c) -> (a -> f a) ! ! ! ! !
  51. 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) x . y :: Lens a c :: (c -> f c) -> (a -> f a) ! (.) :: (i -> j) -> (h -> i) -> h -> j ! ! !
  52. 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) x . y :: Lens a c :: (c -> f c) -> (a -> f a) ! (.) :: (i -> j) -> (h -> i) -> h -> j ! x :: i -> j !
  53. 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) x . y :: Lens a c :: (c -> f c) -> (a -> f a) ! (.) :: (i -> j) -> (h -> i) -> h -> j ! x :: i -> j, y :: h -> i !
  54. 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) x . y :: Lens a c :: (c -> f c) -> (a -> f a) ! (.) :: (i -> j) -> (h -> i) -> h -> j ! x :: i -> j, y :: h -> i i :: b -> f b
  55. 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) x . y :: Lens a c :: (c -> f c) -> (a -> f a) ! (.) :: (i -> j) -> (h -> i) -> h -> j ! x :: i -> j, y :: h -> i i :: b -> f b, j :: a -> f a
  56. 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) x . y :: Lens a c :: (c -> f c) -> (a -> f a) ! (.) :: (i -> j) -> (h -> i) -> h -> j ! x :: i -> j, y :: h -> i i :: b -> f b, j :: a -> f a, h :: c -> f c
  57. 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) x . y :: Lens a c :: (c -> f c) -> (a -> f a) ! (.) :: (i -> j) -> (h -> i) -> h -> j ! x :: i -> j, y :: h -> i i :: b -> f b, j :: a -> f a, h :: c -> f c x . y :: h -> j
  58. 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) x . y :: Lens a c :: (c -> f c) -> (a -> f a) ! (.) :: (i -> j) -> (h -> i) -> h -> j ! x :: i -> j, y :: h -> i i :: b -> f b, j :: a -> f a, h :: c -> f c x . y :: h -> j :: (c -> f c) -> (a -> f a)
  59. 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) x . y :: Lens a c :: (c -> f c) -> (a -> f a) ! (.) :: (i -> j) -> (h -> i) -> h -> j ! x :: i -> j, y :: h -> i i :: b -> f b, j :: a -> f a, h :: c -> f c x . y :: h -> j :: (c -> f c) -> (a -> f a)
  60. ! type Lens a a’ b b’ = forall f.

    Functor f => (b -> f b’) -> a -> f a’ ! type Lens’ a b = Lens a a b b polymorphic update
  61. ! type Lens a a’ b b’ = forall f.

    Functor f => (b -> f b’) -> a -> f a’ ! type Simple f a b = f a a b b ! type Lens’ = Simple Lens polymorphic update
  62. ! type Lens a a’ b b’ = forall f.

    Functor f => (b -> f b’) -> a -> f a’ polymorphic update set :: Lens’ a b -> b -> a -> a set l b = runIdentity . l (const $ Identity b) ! get :: Lens’ a b -> a -> b get l = runConst . l Const ! modify :: Lens’ a b -> (b -> b) -> a modify l f = runIdentity . l (Identity . f) !
  63. ! type Lens a a’ b b’ = forall f.

    Functor f => (b -> f b’) -> a -> f a’ polymorphic update set :: Lens a a’ b b’ -> b’ -> a -> a’ set l b = runIdentity . l (const $ Identity b) ! get :: Lens a a’ b b’ -> a -> b’ get l = runConst . l Const ! modify :: Lens a a’ b b’ -> (b -> b’) -> a’ modify l f = runIdentity . l (Identity . f) !
  64. ! type Lens a a’ b b’ = forall f.

    Functor f => (b -> f b’) -> a -> f a’ polymorphic update set :: Lens a a’ b b’ -> b’ -> a -> a’ set l b = runIdentity . l (const $ Identity b) ! get :: Lens a a’ b b’ -> a -> b’ get l = runConst . l Const ! modify :: Lens a a’ b b’ -> (b -> b’) -> a’ modify l f = runIdentity . l (Identity . f) !
  65. functional elegance even more

  66. 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
  67. 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 lens . getter :: getter lens . setter :: setter getter . setter :: not a program
  68. multiplate type Fold a b = forall f. (Contravariant f,

    Applicative f) => (b -> f b) -> a -> f a ! type Traversal a a’ b b’ = forall f. Applicative f => (b -> f b’) -> a -> f a’
  69. multiplate type Fold a b = forall f. (Contravariant f,

    Applicative f) => (b -> f b) -> a -> f a ! type Traversal a a’ b b’ = forall f. Applicative f => (b -> f b’) -> a -> f a’ getter :: fold traversal :: fold lens :: traversal
  70. type Traversal a a’ b b’ = forall f. Applicative

    f => (b -> f b’) -> a -> f a’ multiplate set :: Traversal a a’ b b’ -> b’ -> a -> a’ set l b = runIdentity . l (const $ Identity b) ! get :: Traversal a a’ b b’ -> a -> b’ get l = runConst . l Const ! modify :: Traversal a a’ b b’ -> (b -> b’) -> a’ modify l f = runIdentity . l (Identity . f) !
  71. type Prism s t a b = forall p f.

    (Choice p, Applicative f) => p a (f b) -> p s (f t) prisms / co-lens prism :: traversal traversal :: fold lens :: traversal
  72. type Iso s t a b = forall p f.

    (Profunctor p, Functor f) => p a (f b) -> p s (f t) iso iso :: lens iso :: prism iso :: traversal iso :: getter iso :: setter
  73. code

  74. gotchas

  75. 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
  76. fin @markhibberd