from typing import Callable, Iterable, List, Optional, Type
from .. import optics
from ..typevars import S, T, A, B, X, Y
from .base import BaseUiLens
[docs]class UnboundLens(BaseUiLens[S, T, A, B]):
'An unbound lens is a lens that has not been bound to any state.'
__slots__ = ('_optic',)
def __init__(self, optic):
self._optic = optic
def __repr__(self) -> str:
return 'UnboundLens({!r})'.format(self._optic)
[docs] def get(self) -> Callable[[S], B]:
'''Get the first value focused by the lens.
>>> from lenses import lens
>>> getter = lens.get()
>>> getter([1, 2, 3])
[1, 2, 3]
>>> zero_item_getter = lens[0].get()
>>> zero_item_getter([1, 2, 3])
1
'''
def getter(state):
return self._optic.to_list_of(state)[0]
return getter
[docs] def collect(self) -> Callable[[S], List[B]]:
'''Get multiple values focused by the lens. Returns them as
a list.
>>> from lenses import lens
>>> collect_each_first = lens.Each()[0].collect()
>>> collect_each_first([(1, 2), (3, 4), (5, 6)])
[1, 3, 5]
'''
def getter(state):
return self._optic.to_list_of(state)
return getter
[docs] def get_monoid(self) -> Callable[[S], B]:
'''Get the values focused by the lens, merging them together by
treating them as a monoid. See `lenses.typeclass.mappend`.
>>> from lenses import lens
>>> get_each_monoidally = lens.Each().get_monoid()
>>> get_each_monoidally([[], [1], [2, 3]])
[1, 2, 3]
'''
def getter(state):
return self._optic.view(state)
return getter
[docs] def set(self, newvalue: B) -> Callable[[S], T]:
'''Set the focus to `newvalue`.
>>> from lenses import lens
>>> set_item_one_to_four = lens[1].set(4)
>>> set_item_one_to_four([1, 2, 3])
[1, 4, 3]
'''
def setter(state):
return self._optic.set(state, newvalue)
return setter
[docs] def set_many(self, new_values: Iterable[B]) -> Callable[[S], T]:
'''Set many foci to values taken by iterating over `new_values`.
>>> from lenses import lens
>>> lens.Each().set_many(range(4, 7))([0, 1, 2])
[4, 5, 6]
'''
def setter_many(state):
return self._optic.iterate(state, new_values)
return setter_many
[docs] def modify(self, func: Callable[[A], B]) -> Callable[[S], T]:
'''Apply a function to the focus.
>>> from lenses import lens
>>> convert_item_one_to_string = lens[1].modify(str)
>>> convert_item_one_to_string([1, 2, 3])
[1, '2', 3]
>>> add_ten_to_item_one = lens[1].modify(lambda n: n + 10)
>>> add_ten_to_item_one([1, 2, 3])
[1, 12, 3]
'''
def modifier(state):
return self._optic.over(state, func)
return modifier
[docs] def construct(self, focus: A) -> S:
'''Construct a state given a focus.'''
return self._optic.re().view(focus)
[docs] def flip(self) -> 'UnboundLens[A, B, S, T]':
'''Flips the direction of the lens. The lens must be unbound
and all the underlying operations must be isomorphisms.
>>> from lenses import lens
>>> json_encoder = lens.Decode().Json().flip()
>>> json_encode = json_encoder.get()
>>> json_encode(['hello', 'world']) # doctest: +SKIP
b'["hello", "world"]'
'''
return UnboundLens(self._optic.re())
def __and__(
self, other: 'UnboundLens[A, B, X, Y]'
) -> 'UnboundLens[S, T, X, Y]':
'''Refine the current focus of this lens by composing it with
another lens object. The other lens must be unbound.
>>> from lenses import lens
>>> first = lens[0]
>>> second = lens[1]
>>> second_first = second & first
>>> get_second_then_first = second_first.get()
>>> get_second_then_first([[0, 1], [2, 3]])
2
'''
if not isinstance(other, UnboundLens):
message = 'Cannot compose lens of type {!r}.'
raise TypeError(message.format(type(other)))
return self._compose_optic(other._optic)
def __get__(
self, instance: Optional[S], owner: Type
) -> BaseUiLens[S, T, A, B]:
if instance is None:
return self
return BoundLens(instance, self._optic)
def _compose_optic(
self, optic: optics.LensLike
) -> 'UnboundLens[S, T, X, Y]':
return UnboundLens(self._optic.compose(optic))
[docs] def kind(self) -> str:
'Returns the "kind" of the lens.'
return self._optic.kind().__name__
add_lens = __and__
class BoundLens(BaseUiLens[S, T, A, B]):
'A bound lens is a lens that has been bound to a specific state.'
__slots__ = ('_state', '_optic')
def __init__(self, state: S, optic: optics.LensLike) -> None:
self._state = state
self._optic = optic
def __repr__(self) -> str:
return 'BoundLens({!r}, {!r})'.format(self._state, self._optic)
def get(self) -> B:
'''Get the first value focused by the lens.
>>> from lenses import bind
>>> bind([1, 2, 3]).get()
[1, 2, 3]
>>> bind([1, 2, 3])[0].get()
1
'''
return self._optic.to_list_of(self._state)[0]
def collect(self) -> List[B]:
'''Get multiple values focused by the lens. Returns them as
a list.
>>> from lenses import bind
>>> bind([(1, 2), (3, 4), (5, 6)]).Each()[0].collect()
[1, 3, 5]
'''
return self._optic.to_list_of(self._state)
def get_monoid(self) -> B:
'''Get the values focused by the lens, merging them together by
treating them as a monoid. See `lenses.typeclass.mappend`.
>>> from lenses import bind
>>> bind([[], [1], [2, 3]]).Each().get_monoid()
[1, 2, 3]
'''
return self._optic.view(self._state)
def set(self, newvalue: B) -> T:
'''Set the focus to `newvalue`.
>>> from lenses import bind
>>> bind([1, 2, 3])[1].set(4)
[1, 4, 3]
'''
return self._optic.set(self._state, newvalue)
def set_many(self, new_values: Iterable[B]) -> T:
'''Set many foci to values taken by iterating over `new_values`.
>>> from lenses import bind
>>> bind([0, 1, 2]).Each().set_many(range(4, 7))
[4, 5, 6]
'''
return self._optic.iterate(self._state, new_values)
def modify(self, func: Callable[[A], B]) -> T:
'''Apply a function to the focus.
>>> from lenses import bind
>>> bind([1, 2, 3])[1].modify(str)
[1, '2', 3]
>>> bind([1, 2, 3])[1].modify(lambda n: n + 10)
[1, 12, 3]
'''
return self._optic.over(self._state, func)
def __and__(
self, other: UnboundLens[A, B, X, Y]
) -> 'BoundLens[S, T, X, Y]':
'''Refine the current focus of this lens by composing it with
another lens object. The other lens must be unbound.
>>> from lenses import lens, bind
>>> first = lens[0]
>>> second = bind([[0, 1], [2, 3]])[1]
>>> (second & first).get()
2
'''
if not isinstance(other, UnboundLens):
message = 'Cannot compose lens of type {!r}.'
raise TypeError(message.format(type(other)))
return self._compose_optic(other._optic)
def _compose_optic(
self, optic: optics.LensLike
) -> 'BoundLens[S, T, X, Y]':
return BoundLens(self._state, self._optic.compose(optic))
def kind(self) -> str:
'Returns the "kind" of the lens.'
return self._optic.kind().__name__
add_lens = __and__
__all__ = ['UnboundLens', 'BoundLens']