A lens library for python that enables immutable manipulation of deeply nested data structures
npx @tessl/cli install tessl/pypi-lenses@1.2.0A comprehensive Python lens library that enables immutable manipulation of deeply nested data structures without mutation. Inspired by Haskell lenses but adapted for Python's syntax and conventions, providing a functional programming approach to data transformation through a chainable API.
pip install lensespip install pyrsistent (for enhanced immutable data structures)from lenses import lens, bindFor advanced usage:
from lenses import lens, bind, UnboundLens
from lenses.maybe import Just, Nothingfrom lenses import lens, bind
# Basic lens operations on lists
data = [1, 2, 3, 4, 5]
# Get values
first_item = lens[0].get()(data) # 1
all_items = lens.Each().collect()(data) # [1, 2, 3, 4, 5]
# Set values (returns new list)
new_data = lens[1].set(99)(data) # [1, 99, 3, 4, 5]
# Modify values with functions
incremented = lens.Each().modify(lambda x: x + 1)(data) # [2, 3, 4, 5, 6]
# Working with dictionaries
person = {'name': 'Alice', 'age': 30, 'address': {'city': 'Boston', 'state': 'MA'}}
# Access nested data
city = lens['address']['city'].get()(person) # 'Boston'
# Update nested data immutably
updated_person = lens['address']['city'].set('Cambridge')(person)
# {'name': 'Alice', 'age': 30, 'address': {'city': 'Cambridge', 'state': 'MA'}}
# Using bound lenses
bound_lens = bind(person)
age = bound_lens['age'].get() # 30
older_person = bound_lens['age'].modify(lambda x: x + 1)Lenses provides a layered architecture for immutable data manipulation:
UnboundLens and BoundLens classes providing the main user interfaceThis design enables composition of complex data access patterns while maintaining immutability and type safety.
The fundamental lens interface providing get, set, modify operations and lens composition through the & operator.
class UnboundLens:
def get(self) -> StateFunction[S, B]: ...
def set(self, newvalue: B) -> StateFunction[S, T]: ...
def modify(self, func: Callable[[A], B]) -> StateFunction[S, T]: ...
def __and__(self, other) -> UnboundLens: ...
class BoundLens:
def get(self) -> B: ...
def set(self, newvalue: B) -> T: ...
def modify(self, func: Callable[[A], B]) -> T: ...
def bind(state: S) -> BoundLens[S, S, S, S]: ...Comprehensive collection of lens constructors for accessing different data structures and implementing various access patterns including containers, objects, traversals, and transformations.
def Each(self) -> BaseUiLens: ...
def GetItem(self, key: Any) -> BaseUiLens: ...
def GetAttr(self, name: str) -> BaseUiLens: ...
def Filter(self, predicate: Callable[[A], bool]) -> BaseUiLens: ...
def Items(self) -> BaseUiLens: ...
def Values(self) -> BaseUiLens: ...
def Keys(self) -> BaseUiLens: ...
def Json(self) -> BaseUiLens: ...Low-level optics system providing the mathematical foundation with different optic types (Lens, Prism, Traversal, Isomorphism, etc.) that can be composed to create complex data access patterns.
class Lens: ...
class Prism: ...
class Traversal: ...
class Isomorphism: ...
class Fold: ...
class Setter: ...Supporting types including Maybe monad for optional values, type class system for functional programming patterns, and hooks system for extending support to custom data types.
class Just:
def __init__(self, item: A) -> None: ...
def map(self, fn: Callable[[A], B]) -> Just[B]: ...
def maybe(self, guard: B) -> Union[A, B]: ...
class Nothing(Just): ...
def mempty(monoid: Any) -> Any: ...
def mappend(monoid: Any, other: Any) -> Any: ...lens: UnboundLens # Main lens object, entry point to the libraryfrom typing import TypeVar, Callable, List, Iterable, Union, Optional, Any
S = TypeVar("S") # Source type
T = TypeVar("T") # Target type
A = TypeVar("A") # Focus input type
B = TypeVar("B") # Focus output type
StateFunction[S, T] = Callable[[S], T]