0
# Optics System
1
2
Low-level optics system providing the mathematical foundation for lens operations. The optics system implements different types of optics (Lens, Prism, Traversal, Isomorphism, etc.) that can be composed to create complex data access patterns.
3
4
## Capabilities
5
6
### Base Optics Interface
7
8
Core interfaces and base classes that define the optics system.
9
10
```python { .api }
11
class LensLike:
12
"""Base interface for all optic types."""
13
def kind(self): ...
14
def compose(self, other): ...
15
def view(self, state): ...
16
def over(self, state, func): ...
17
def set(self, state, value): ...
18
def to_list_of(self, state): ...
19
20
class TrivialIso:
21
"""Identity isomorphism - does nothing."""
22
pass
23
24
class ComposedLens:
25
"""Composed optic combining two optics."""
26
def __init__(self, lens1, lens2): ...
27
28
class Equality:
29
"""Equality-based optic."""
30
pass
31
32
class TupleOptic:
33
"""Combines multiple optic focuses into a tuple."""
34
def __init__(self, *optics): ...
35
```
36
37
### Optic Types by Capability
38
39
Different optic types providing various levels of access capability.
40
41
```python { .api }
42
class Fold:
43
"""Read-only access to multiple foci."""
44
def __init__(self, func: Callable[[A], Iterable[X]]): ...
45
46
class Getter:
47
"""Read-only access to single focus."""
48
def __init__(self, getter: Callable[[A], X]): ...
49
50
class Setter:
51
"""Write-only access to foci."""
52
pass
53
54
class Review:
55
"""Write-only optic for constructing values."""
56
pass
57
58
class Traversal:
59
"""Read-write access to multiple foci."""
60
def __init__(self, folder: Callable[[A], Iterable[X]],
61
builder: Callable[[A, Iterable[Y]], B]): ...
62
63
class Lens:
64
"""Read-write access to single focus."""
65
def __init__(self, getter: Callable[[A], X],
66
setter: Callable[[A, Y], B]): ...
67
68
class Prism:
69
"""Optional read-write access (zero or one focus)."""
70
def __init__(self, unpack: Callable[[A], Just[X]],
71
pack: Callable[[Y], B]): ...
72
73
class Isomorphism:
74
"""Bidirectional conversion between types."""
75
def __init__(self, forwards: Callable[[A], X],
76
backwards: Callable[[Y], B]): ...
77
def re(self): ... # Reverse the isomorphism
78
```
79
80
### Concrete Fold Implementations
81
82
Specific fold implementations for read-only traversal.
83
84
```python { .api }
85
class IterableFold:
86
"""Fold over any iterable object."""
87
pass
88
```
89
90
#### Usage Examples
91
92
```python
93
from lenses.optics import IterableFold
94
from lenses import lens
95
96
# Direct optic usage (advanced)
97
fold = IterableFold()
98
data = [1, 2, 3, 4, 5]
99
values = fold.to_list_of(data) # [1, 2, 3, 4, 5]
100
101
# More commonly used through lens interface
102
lens.Iter().collect()(data) # [1, 2, 3, 4, 5]
103
```
104
105
### Concrete Isomorphism Implementations
106
107
Isomorphisms for converting between different data representations.
108
109
```python { .api }
110
class DecodeIso:
111
"""String/bytes encoding conversion isomorphism."""
112
def __init__(self, encoding: str = "utf-8", errors: str = "strict"): ...
113
114
class JsonIso:
115
"""JSON string parsing isomorphism."""
116
pass
117
118
class NormalisingIso:
119
"""Value normalization isomorphism."""
120
def __init__(self, setter: Callable[[A], X]): ...
121
122
class ErrorIso:
123
"""Exception-raising isomorphism for debugging."""
124
def __init__(self, exception: Exception, message: Optional[str] = None): ...
125
```
126
127
#### Usage Examples
128
129
```python
130
from lenses.optics import JsonIso, DecodeIso
131
from lenses import lens
132
133
# JSON isomorphism
134
json_iso = JsonIso()
135
json_data = '{"name": "Alice", "age": 30}'
136
parsed = json_iso.view(json_data) # {"name": "Alice", "age": 30}
137
138
# Through lens interface (more common)
139
name = lens.Json()["name"].get()(json_data) # "Alice"
140
141
# Decode isomorphism
142
decode_iso = DecodeIso("utf-8")
143
byte_data = b"hello"
144
text = decode_iso.view(byte_data) # "hello"
145
```
146
147
### Concrete Prism Implementations
148
149
Prisms for optional or conditional access patterns.
150
151
```python { .api }
152
class FilteringPrism:
153
"""Prism that filters by predicate."""
154
def __init__(self, predicate: Callable[[A], bool]): ...
155
156
class InstancePrism:
157
"""Prism that filters by type."""
158
def __init__(self, type_: Type): ...
159
160
class JustPrism:
161
"""Prism for Maybe Just values."""
162
pass
163
```
164
165
#### Usage Examples
166
167
```python
168
from lenses.optics import FilteringPrism, InstancePrism
169
from lenses import lens
170
171
# Filtering prism
172
positive_prism = FilteringPrism(lambda x: x > 0)
173
data = [-1, 2, -3, 4]
174
positive_values = [positive_prism.to_list_of([x]) for x in data] # [[], [2], [], [4]]
175
176
# Through lens interface (more common)
177
positive = lens.Each().Filter(lambda x: x > 0).collect()(data) # [2, 4]
178
179
# Instance prism for type filtering
180
str_prism = InstancePrism(str)
181
mixed = [1, "hello", 2.5, "world"]
182
strings = lens.Each().Instance(str).collect()(mixed) # ["hello", "world"]
183
```
184
185
### Concrete Setter Implementations
186
187
Setters for write-only operations.
188
189
```python { .api }
190
class ForkedSetter:
191
"""Parallel composition of multiple setters."""
192
def __init__(self, *setters): ...
193
```
194
195
#### Usage Examples
196
197
```python
198
from lenses.optics import ForkedSetter, GetitemLens
199
from lenses import lens
200
201
# Forked setter for parallel updates
202
fork = ForkedSetter(GetitemLens(0), GetitemLens(2))
203
data = [1, 2, 3, 4, 5]
204
updated = fork.set(data, 99) # [99, 2, 99, 4, 5]
205
206
# Through lens interface (more common)
207
fork_lens = lens.Fork(lens[0], lens[2])
208
result = fork_lens.set(99)(data) # [99, 2, 99, 4, 5]
209
```
210
211
### Concrete Traversal Implementations
212
213
Traversals for read-write access to multiple foci.
214
215
```python { .api }
216
class EachTraversal:
217
"""Traverse all items in a collection."""
218
pass
219
220
class ItemsTraversal:
221
"""Traverse dictionary items as (key, value) pairs."""
222
pass
223
224
class RecurTraversal:
225
"""Recursively traverse objects of a specific type."""
226
def __init__(self, cls): ...
227
228
class RegexTraversal:
229
"""Traverse string parts matching a regex pattern."""
230
def __init__(self, pattern: Pattern, flags: int = 0): ...
231
232
class GetZoomAttrTraversal:
233
"""Traverse attribute, zooming if it's a lens."""
234
def __init__(self, name: str): ...
235
236
class ZoomAttrTraversal:
237
"""Traverse lens attribute."""
238
def __init__(self, name: str): ...
239
240
class ZoomTraversal:
241
"""Follow bound lens objects."""
242
pass
243
```
244
245
#### Usage Examples
246
247
```python
248
from lenses.optics import EachTraversal, RecurTraversal
249
from lenses import lens
250
251
# Each traversal
252
each = EachTraversal()
253
data = [1, 2, 3]
254
values = each.to_list_of(data) # [1, 2, 3]
255
doubled = each.over(data, lambda x: x * 2) # [2, 4, 6]
256
257
# Recursive traversal
258
recur = RecurTraversal(int)
259
nested = [1, [2, 3], [[4]], 5]
260
all_ints = recur.to_list_of(nested) # [1, 2, 3, 4, 5]
261
262
# Through lens interface (more common)
263
all_ints_lens = lens.Recur(int).collect()(nested) # [1, 2, 3, 4, 5]
264
```
265
266
### Concrete Lens Implementations
267
268
True lenses providing read-write access to single foci.
269
270
```python { .api }
271
class GetattrLens:
272
"""Lens for object attributes."""
273
def __init__(self, name: str): ...
274
275
class GetitemLens:
276
"""Lens for container items."""
277
def __init__(self, key: Any): ...
278
279
class GetitemOrElseLens:
280
"""Lens for container items with default values."""
281
def __init__(self, key: Any, default: Optional[Y] = None): ...
282
283
class ContainsLens:
284
"""Lens for collection containment as boolean."""
285
def __init__(self, item: A): ...
286
287
class ItemLens:
288
"""Lens for dictionary items as (key, value) pairs."""
289
def __init__(self, key: Any): ...
290
291
class ItemByValueLens:
292
"""Lens for dictionary items by value."""
293
def __init__(self, value: Any): ...
294
295
class PartsLens:
296
"""Converts fold/traversal to lens by collecting parts."""
297
def __init__(self, optic): ...
298
```
299
300
#### Usage Examples
301
302
```python
303
from lenses.optics import GetitemLens, GetattrLens, ContainsLens
304
from lenses import lens
305
306
# Getitem lens
307
item_lens = GetitemLens(0)
308
data = [10, 20, 30]
309
first = item_lens.view(data) # 10
310
updated = item_lens.set(data, 99) # [99, 20, 30]
311
312
# Getattr lens
313
from collections import namedtuple
314
Person = namedtuple('Person', 'name age')
315
person = Person("Alice", 30)
316
name_lens = GetattrLens('name')
317
name = name_lens.view(person) # "Alice"
318
319
# Contains lens
320
contains_lens = ContainsLens(2)
321
data = [1, 2, 3]
322
has_two = contains_lens.view(data) # True
323
without_two = contains_lens.set(data, False) # [1, 3]
324
325
# Through lens interface (more common)
326
first_item = lens[0].get()(data) # 10
327
person_name = lens.GetAttr('name').get()(person) # "Alice"
328
```
329
330
### Optic Composition
331
332
How optics compose to create complex access patterns.
333
334
```python { .api }
335
def compose_optics(optic1, optic2):
336
"""Compose two optics into a single optic."""
337
return ComposedLens(optic1, optic2)
338
```
339
340
#### Composition Examples
341
342
```python
343
from lenses.optics import GetitemLens, EachTraversal
344
from lenses import lens
345
346
# Manual composition (advanced usage)
347
first_item = GetitemLens(0)
348
each_item = EachTraversal()
349
composed = first_item.compose(each_item)
350
351
# Through lens interface (recommended)
352
data = [[1, 2], [3, 4], [5, 6]]
353
first_each = lens[0].Each().collect()(data) # [1, 2]
354
355
# Complex compositions
356
nested_access = (lens["users"]
357
.Each()
358
.Filter(lambda u: u.get("active", False))
359
["profile"]
360
["name"])
361
```