Lenses, Prisms and Traversable
25.04.2020
lens
is a package that allows you to focus deeply into a complex structure and retrieve or modify the elements. Lenses work in a similar way to how the imperative style of programming would retrieve elements from a complex object.
In languages like C++, Java, Javascript, Python etc. we would use something like person.children[0].pet.age = 4
, but in Haskell without lenses we would have to do some ugly nesting such as person { children = { ... } }
.
What the library Control.Lens
allowes us to do it make something like person.children.ix 0.pet.age .~ 4
There are many useful operators such as
object ^. lenses -- get single value
object ^.. lenses -- get list of values
object ^? lenses -- maybe get a value
object & lenses .~ value -- set a value
object & lenses %~ function -- apply a function to the value
object & lenses %%~ function -- apply a function modifying the result structure
object & lenses +~ number -- increment by number
object & lenses <>~ value -- append (<>) a value
There are also many predefined lenses such as
_1, _2, ..., both, each -- get n-th values
_Just, _Right, _Left -- apply constructors
ix n -- get n-th value or key in map
traverse -- get all values one-by-one
to f -- apply a result function
filtered p -- filter by predicate
_head, _last, _init, _tail -- traverse parts of a list
worded, lined -- traverse over words / lines
Lets look at an example of how this can be used
First we need to define some example
("hello", "world") ^. _1 ~> "hello"
[1, 2, 3, 4, 5] ^. _init ~> [1, 2, 3, 4]
[1, 2, 3, 4, 5] ^.. _last ~> [5]
[1, 2, 3, 4, 5] ^.. _init ~> [[1, 2, 3, 4]]
("hello", "world") & _1 .~ 5 ~> (5, "world")
("Hello", "world") & _2 %~ (++ "!!") ~> ("Hello", "world!!")
("Hello", "world") ^ _2 %%~ id ~> [("Hello",'w'),("Hello",'o'),("Hello",'r'),("Hello",'l'),("Hello",'d')]
[1, 2, 3, 4, 5] & _head +~ 5 ~> [6, 2, 3, 1, 5]
[1, 2, 3, 4, 5] & _init <>~ [6, 7] ~> [1, 2, 3, 4, 6, 7]
[1, 2, 3, 4, 5] & traverse %~ show ~> ["1", "2", "3", "4", "5"]