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]}
>>> 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).