Pro Yearly is on sale from $80 to $50! »

lens: from the ground up

lens: from the ground up

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)

42d9867a0fee0fa6de6534e9df0f1e9b?s=128

Mark Hibberd

February 26, 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. 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
  20. 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
  21. ! type Lens’ a b = forall f. Functor f

    => (b -> f b) -> a -> f a back to the drawing board
  22. ! 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?”
  23. ! 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 ! !
  24. ! 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 !
  25. ! 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
  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 y = l x -- :: a -> Identity a z = y a -- :: Identity a in runIdentity z
  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 = runIdentity (l (const $ Identity b) a) ! ! !
  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 = runIdentity . l (const $ Identity b) $ a ! ! !
  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 = runIdentity . l (const $ Identity b) ! ! !
  30. ! 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 ! ! !
  31. ! 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 ! ! !
  32. ! 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
  33. ! 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 ! !
  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 = let x = Const -- :: b -> Const b b y = l x -- :: a -> Const b a in 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 = let x = Const -- :: b -> Const b b y = l x -- :: a -> Const b a z = y a -- :: Const b a in undefined
  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 y = l x -- :: a -> Const b a z = y a -- :: Const b a in runConst z
  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 = runConst (l Const a) ! ! !
  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 = runConst . l Const $ a ! ! !
  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 = runConst . l Const ! ! !
  40. ! 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 ! ! ! !
  41. ! 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) !
  42. functional elegance

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

    f => (b -> f b) -> a -> f a
  44. 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 ! ! ! ! !
  45. 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 ! ! ! ! !
  46. 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 ! ! ! ! !
  47. 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) ! ! ! ! !
  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 :: (c -> f c) -> (b -> f b) x . y :: Lens a c :: (c -> f c) -> (a -> f a) ! (.) :: (i -> j) -> (h -> i) -> h -> j ! ! !
  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 :: (c -> f c) -> (a -> f a) ! (.) :: (i -> j) -> (h -> i) -> h -> j ! x :: i -> j !
  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) ! (.) :: (i -> j) -> (h -> i) -> h -> j ! x :: i -> j, y :: h -> i !
  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 ! x :: i -> j, y :: h -> i i :: b -> f b
  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, y :: h -> i i :: b -> f b, j :: a -> f a
  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 i :: b -> f b, j :: a -> f a, h :: c -> f c
  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, j :: a -> f a, h :: c -> f c x . y :: h -> j
  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, h :: c -> f c x . y :: h -> j :: (c -> f c) -> (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 x . y :: h -> j :: (c -> f c) -> (a -> f a)
  57. ! 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
  58. ! 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
  59. ! 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) !
  60. ! 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) !
  61. ! 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) !
  62. 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
  63. multiplate type Traversal a a’ b b’ = forall f.

    Applicative f => (b -> f b’) -> a -> f a’
  64. 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) !
  65. type Prism s t a b = forall p f.

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

    (Profunctor p, Functor f) => p a (f b) -> p s (f t) iso
  67. code

  68. gotchas

  69. 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
  70. fin @markhibberd