Lens methods¶
So far we’ve seen lenses that extract data out of data-structures, but lenses are more powerful than that. Lenses can actually perform arbitrary computation on the data passing through them as long as that computation can be reversed.
A simple example is that of the Item
method which returns a lens that
focuses on a single key of a dictionary but returns both the key and the
value:
>>> from lenses import lens
>>> item_one = lens.Item('one')
>>> item_one.get()({'one': 1})
('one', 1)
>>> item_one.set(('three', 3))({'one': 1})
{'three': 3}
For a good example of a more complex lens, check out the Json
method
which gives you a lens that can focus a string as though it were a parsed
json object.
>>> data = '{"numbers":[1, 2, 3]}'
>>> json_lens = lens.Json()
>>> json_lens.get()(data) == {'numbers': [1, 2, 3]}
True
>>> json_lens['numbers'][1].set(4)(data)
'{"numbers": [1, 4, 3]}'
At their heart, lenses are really just souped-up getters and setters. If
you have a getter and a setter for some data then you can turn those
into a lens using the Lens
method. Here is how you could recreate the
Item('one')
lens defined above in terms of Lens
:
>>> def getter(current_state):
... return 'one', current_state['one']
...
>>> def setter(old_state, new_focus):
... key, value = new_focus
... new_state = old_state.copy()
... del new_state['one']
... new_state[key] = value
... return new_state
...
>>> item_one = lens.Lens(getter, setter)
>>> item_one.get()({'one': 1})
('one', 1)
>>> item_one.set(('three', 3))({'one': 1})
{'three': 3}
Recreating existing behaviour isn’t very useful, but hopefully you can see how useful it is to be able to make your own lenses just by writing a pair of functions.
If you use custom lenses frequently then you may want to look into the
Iso
method which is a less powerful but often more convenient version
of Lens
.
There are a number of such more complicated lenses defined on lens
objects. To help avoid collision with accessing attributes on the state,
their names are all in CamelCase. See help(lenses.UnboundLens)
in
the repl for more. If you need to access an attribute on the state
that has been shadowed by one of lens’ methods then you can use
my_lens.GetAttr(attribute)
.