or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-lenses.mdindex.mdlens-constructors.mdoptics.mdutility-types.md

lens-constructors.mddocs/

0

# Lens Constructors

1

2

Comprehensive collection of lens constructors for accessing different data structures and implementing various access patterns. These methods are available on both UnboundLens and BoundLens classes, providing building blocks for complex data manipulation.

3

4

## Capabilities

5

6

### Container Access

7

8

Methods for accessing elements within containers like lists, dictionaries, and objects.

9

10

```python { .api }

11

def GetItem(self, key: Any) -> BaseUiLens:

12

"""Focus an item inside a container. Analogous to operator.itemgetter."""

13

14

def GetAttr(self, name: str) -> BaseUiLens:

15

"""Focus an attribute of an object. Analogous to getattr."""

16

17

def Get(self, key: Any, default: Optional[Y] = None) -> BaseUiLens:

18

"""Focus an item with default value for missing keys. Analogous to dict.get."""

19

20

def Contains(self, item: A) -> BaseUiLens[S, S, bool, bool]:

21

"""Focus a boolean indicating whether state contains item."""

22

```

23

24

#### Usage Examples

25

26

```python

27

from lenses import lens

28

29

# GetItem (also available as [key] syntax)

30

data = [1, 2, 3]

31

lens.GetItem(0).get()(data) # 1

32

lens[0].get()(data) # Same as above

33

34

# Dictionary access

35

person = {"name": "Alice", "age": 30}

36

lens["name"].get()(person) # "Alice"

37

38

# GetAttr for object attributes

39

from collections import namedtuple

40

Person = namedtuple('Person', 'name age')

41

p = Person("Bob", 25)

42

lens.GetAttr('name').get()(p) # "Bob"

43

44

# Get with default values

45

data = {"a": 1}

46

lens.Get("b", 0).get()(data) # 0 (default)

47

lens.Get("b").set(2)(data) # {"a": 1, "b": 2}

48

49

# Contains

50

data = [1, 2, 3]

51

lens.Contains(2).get()(data) # True

52

lens.Contains(2).set(False)(data) # [1, 3] (removes item)

53

```

54

55

### Traversal and Iteration

56

57

Methods for working with multiple elements in collections.

58

59

```python { .api }

60

def Each(self) -> BaseUiLens:

61

"""Traverse all items in an iterable. Analogous to iter."""

62

63

def Items(self) -> BaseUiLens:

64

"""Traverse key-value pairs in a dictionary. Analogous to dict.items."""

65

66

def Keys(self) -> BaseUiLens:

67

"""Traverse dictionary keys. Analogous to dict.keys."""

68

69

def Values(self) -> BaseUiLens:

70

"""Traverse dictionary values. Analogous to dict.values."""

71

72

def Iter(self) -> BaseUiLens:

73

"""Read-only fold over any iterable (cannot set values)."""

74

```

75

76

#### Usage Examples

77

78

```python

79

from lenses import lens

80

81

# Each - traverse all items

82

data = [1, 2, 3, 4, 5]

83

lens.Each().collect()(data) # [1, 2, 3, 4, 5]

84

lens.Each().modify(lambda x: x * 2)(data) # [2, 4, 6, 8, 10]

85

86

# Dictionary traversals

87

data = {"a": 1, "b": 2, "c": 3}

88

lens.Items().collect()(data) # [("a", 1), ("b", 2), ("c", 3)]

89

lens.Keys().collect()(data) # ["a", "b", "c"]

90

lens.Values().collect()(data) # [1, 2, 3]

91

lens.Values().modify(lambda x: x + 10)(data) # {"a": 11, "b": 12, "c": 13}

92

93

# Each works with dictionaries by items

94

lens.Each().collect()(data) # [("a", 1), ("b", 2), ("c", 3)]

95

lens.Each()[1].modify(lambda x: x + 1)(data) # {"a": 2, "b": 3, "c": 4}

96

```

97

98

### Filtering and Selection

99

100

Methods for conditionally selecting elements based on predicates or types.

101

102

```python { .api }

103

def Filter(self, predicate: Callable[[A], bool]) -> BaseUiLens:

104

"""Focus values that satisfy the predicate."""

105

106

def Instance(self, type_: Type) -> BaseUiLens:

107

"""Focus values that are instances of the given type."""

108

109

def Just(self) -> BaseUiLens:

110

"""Focus the value inside a Just object (from Maybe type)."""

111

```

112

113

#### Usage Examples

114

115

```python

116

from lenses import lens

117

118

# Filter by predicate

119

data = [1, -2, 3, -4, 5]

120

positive = lens.Each().Filter(lambda x: x > 0)

121

positive.collect()(data) # [1, 3, 5]

122

positive.modify(lambda x: x * 10)(data) # [10, -2, 30, -4, 50]

123

124

# Instance type filtering

125

mixed_data = [1, "hello", 2.5, "world", 42]

126

strings = lens.Each().Instance(str)

127

strings.collect()(mixed_data) # ["hello", "world"]

128

129

# Just for Maybe types

130

from lenses.maybe import Just, Nothing

131

data = [Just(1), Nothing(), Just(3)]

132

values = lens.Each().Just()

133

values.collect()(data) # [1, 3]

134

```

135

136

### Transformations and Isomorphisms

137

138

Methods for converting between different data representations.

139

140

```python { .api }

141

def Json(self) -> BaseUiLens:

142

"""Focus string as parsed JSON data. Analogous to json.loads."""

143

144

def Decode(self, encoding: str = "utf-8", errors: str = "strict") -> BaseUiLens:

145

"""Focus bytes as decoded string. Analogous to bytes.decode."""

146

147

def Iso(self, forwards: Callable[[A], X], backwards: Callable[[Y], B]) -> BaseUiLens:

148

"""Create an isomorphism from forward and backward functions."""

149

150

def Norm(self, setter: Callable[[A], X]) -> BaseUiLens:

151

"""Apply normalization function when setting values."""

152

```

153

154

#### Usage Examples

155

156

```python

157

from lenses import lens

158

159

# JSON parsing

160

json_data = '{"users": [{"name": "Alice"}, {"name": "Bob"}]}'

161

users = lens.Json()["users"].Each()["name"].collect()(json_data) # ["Alice", "Bob"]

162

163

# Update JSON

164

updated = lens.Json()["users"][0]["name"].set("Charlie")(json_data)

165

# '{"users": [{"name": "Charlie"}, {"name": "Bob"}]}'

166

167

# Decode bytes

168

byte_data = b"hello world"

169

text = lens.Decode().get()(byte_data) # "hello world"

170

171

# Custom isomorphisms

172

chr_ord_iso = lens.Iso(chr, ord) # char <-> int conversion

173

lens.Iso(chr, ord).get()(65) # 'A'

174

lens.Iso(chr, ord).set('B')(65) # 66

175

176

# Normalization

177

normalize_int = lens[0].Norm(int)

178

normalize_int.set(4.7)([1, 2, 3]) # [4, 2, 3] (4.7 -> 4)

179

```

180

181

### Advanced Access Patterns

182

183

Specialized lens constructors for complex access patterns.

184

185

```python { .api }

186

def Item(self, key: Any) -> BaseUiLens:

187

"""Focus a dictionary item as (key, value) pair."""

188

189

def ItemByValue(self, value: Any) -> BaseUiLens:

190

"""Focus dictionary item by its value."""

191

192

def Recur(self, cls) -> BaseUiLens:

193

"""Recursively focus all objects of given type."""

194

195

def Regex(self, pattern: Union[str, Pattern], flags: int = 0) -> BaseUiLens:

196

"""Focus parts of string matching regex pattern."""

197

198

def Parts(self) -> BaseUiLens:

199

"""Convert fold/traversal into lens by focusing list of all parts."""

200

```

201

202

#### Usage Examples

203

204

```python

205

from lenses import lens

206

207

# Item as key-value pairs

208

data = {"a": 1, "b": 2}

209

lens.Item("a").get()(data) # ("a", 1)

210

lens.Item("a").set(("a", 10))(data) # {"a": 10, "b": 2}

211

lens.Item("a").set(None)(data) # {"b": 2} (removes item)

212

213

# ItemByValue

214

data = {"x": 10, "y": 20, "z": 10}

215

lens.ItemByValue(10).get()(data) # ("x", 10) (first match)

216

217

# Recursive type traversal

218

data = [1, [2, [3, 4], 5], 6]

219

lens.Recur(int).collect()(data) # [1, 2, 3, 4, 5, 6]

220

221

# Regex patterns

222

text = "The quick brown fox"

223

words = lens.Regex(r'\w+')

224

words.collect()(text) # ["The", "quick", "brown", "fox"]

225

words.modify(lambda w: w.upper())(text) # "THE QUICK BROWN FOX"

226

227

# Parts - convert traversals to lenses

228

nested = [[1, 2], [3, 4], [5, 6]]

229

all_nums = lens.Each().Each().Parts()

230

all_nums.get()(nested) # [1, 2, 3, 4, 5, 6]

231

all_nums[0].set(99)(nested) # [[99, 2], [3, 4], [5, 6]]

232

```

233

234

### Custom Lens Creation

235

236

Methods for creating custom lenses from functions.

237

238

```python { .api }

239

def Lens(self, getter: Callable[[A], X], setter: Callable[[A, Y], B]) -> BaseUiLens:

240

"""Create custom lens from getter and setter functions."""

241

242

def F(self, getter: Callable[[A], X]) -> BaseUiLens:

243

"""Create getter-only lens from function."""

244

245

def Prism(self, unpack: Callable[[A], Just[X]], pack: Callable[[Y], B],

246

ignore_none: bool = False, ignore_errors: Optional[tuple] = None) -> BaseUiLens:

247

"""Create prism from unpack/pack functions with optional error handling."""

248

249

def Traversal(self, folder: Callable[[A], Iterable[X]],

250

builder: Callable[[A, Iterable[Y]], B]) -> BaseUiLens:

251

"""Create custom traversal from folder and builder functions."""

252

253

def Fold(self, func: Callable[[A], Iterable[X]]) -> BaseUiLens:

254

"""Create read-only fold from function returning iterable."""

255

```

256

257

#### Usage Examples

258

259

```python

260

from lenses import lens

261

262

# Custom lens for list average

263

def get_avg(lst):

264

return sum(lst) / len(lst)

265

266

def set_avg(old_lst, new_avg):

267

# Set average by adjusting last element

268

target_sum = new_avg * len(old_lst)

269

return old_lst[:-1] + [target_sum - sum(old_lst[:-1])]

270

271

avg_lens = lens.Lens(get_avg, set_avg)

272

avg_lens.get()([1, 2, 3]) # 2.0

273

avg_lens.set(5)([1, 2, 3]) # [1, 2, 12] (avg is now 5)

274

275

# Getter-only lens

276

abs_lens = lens.Each().F(abs)

277

abs_lens.collect()([-1, 2, -3]) # [1, 2, 3]

278

279

# Custom traversal for list endpoints

280

def ends_folder(lst):

281

yield lst[0]

282

yield lst[-1]

283

284

def ends_builder(old_lst, new_values):

285

new_vals = list(new_values)

286

result = list(old_lst)

287

result[0] = new_vals[0]

288

result[-1] = new_vals[1]

289

return result

290

291

ends_lens = lens.Traversal(ends_folder, ends_builder)

292

ends_lens.collect()([1, 2, 3, 4]) # [1, 4]

293

ends_lens.set(99)([1, 2, 3, 4]) # [99, 2, 3, 99]

294

```

295

296

### Composition and Parallel Operations

297

298

Methods for combining multiple lenses or operations.

299

300

```python { .api }

301

def Fork(self, *lenses: BaseUiLens) -> BaseUiLens:

302

"""Parallel composition of multiple sub-lenses."""

303

304

def Tuple(self, *lenses: BaseUiLens) -> BaseUiLens:

305

"""Combine focuses of multiple lenses into a tuple."""

306

```

307

308

#### Usage Examples

309

310

```python

311

from lenses import lens

312

313

# Fork - parallel operations

314

data = [1, 2, 3, 4, 5]

315

fork_lens = lens.Fork(lens[0], lens[2], lens[4])

316

fork_lens.set(99)(data) # [99, 2, 99, 4, 99]

317

318

# Tuple - combine multiple focuses

319

data = [10, 20, 30, 40]

320

tuple_lens = lens.Tuple(lens[0], lens[3])

321

tuple_lens.get()(data) # (10, 40)

322

tuple_lens.set((1, 9))(data) # [1, 20, 30, 9]

323

324

# Useful with Each for cross-product operations

325

state = ([1, 2], [3, 4])

326

cross = lens.Tuple(lens[0], lens[1]).Each().Each()

327

cross.collect()(state) # [1, 2, 3, 4]

328

```

329

330

### Special and Debugging Lenses

331

332

Utility lenses for debugging and special cases.

333

334

```python { .api }

335

def Error(self, exception: Exception, message: Optional[str] = None) -> BaseUiLens:

336

"""Lens that raises exception when focused (for debugging)."""

337

338

def GetZoomAttr(self, name: str) -> BaseUiLens:

339

"""Focus attribute, zooming if it's a lens itself."""

340

341

def Zoom(self) -> BaseUiLens:

342

"""Follow state as if it were a BoundLens object."""

343

344

def ZoomAttr(self, name: str) -> BaseUiLens:

345

"""Focus attribute and follow it as BoundLens."""

346

```

347

348

#### Usage Examples

349

350

```python

351

from lenses import lens, bind

352

353

# Error lens for debugging

354

debug_lens = lens.Error(ValueError("Debug breakpoint"))

355

# debug_lens.get()(data) # Raises ValueError

356

357

# Zoom lenses work with bound lenses as data

358

data = [bind([1, 2, 3])[1], 4, 5]

359

lens[0].Zoom().get()(data) # 2 (follows the bound lens)

360

lens[0].Zoom().set(99)(data) # [[1, 99, 3], 4, 5]

361

```