or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-collections.mdindex.mdrecords-and-classes.mdtype-checked-collections.mdutilities.md

utilities.mddocs/

0

# Utilities and Transformations

1

2

Helper functions for converting between mutable and immutable structures, applying transformations, and accessing nested data. These utilities bridge the gap between regular Python data structures and persistent collections.

3

4

## Capabilities

5

6

### Freeze/Thaw Conversions

7

8

Convert between mutable Python built-in types and persistent collections, enabling easy integration with existing codebases.

9

10

```python { .api }

11

def freeze(obj):

12

"""

13

Recursively convert mutable Python structures to persistent equivalents.

14

15

Conversions:

16

- dict -> pmap

17

- list -> pvector

18

- set -> pset

19

- tuple -> tuple (preserved)

20

- Other types -> unchanged

21

22

Parameters:

23

- obj: Object to convert

24

25

Returns:

26

Persistent version of the input structure

27

"""

28

29

def thaw(obj):

30

"""

31

Recursively convert persistent structures to mutable Python equivalents.

32

33

Conversions:

34

- pmap -> dict

35

- pvector -> list

36

- pset -> set

37

- tuple -> tuple (preserved)

38

- Other types -> unchanged

39

40

Parameters:

41

- obj: Object to convert

42

43

Returns:

44

Mutable version of the input structure

45

"""

46

47

def mutant(fn) -> callable:

48

"""

49

Decorator that automatically freezes function arguments and return value.

50

51

Useful for integrating persistent data structures with functions that

52

expect mutable inputs or for ensuring immutability of function results.

53

54

Parameters:

55

- fn: Function to decorate

56

57

Returns:

58

Decorated function that freezes args and return value

59

"""

60

```

61

62

### Nested Data Access

63

64

Safely access and manipulate nested data structures using key paths.

65

66

```python { .api }

67

def get_in(keys: Iterable, coll: Mapping, default=None, no_default: bool = False):

68

"""

69

Get value from nested mapping structure using sequence of keys.

70

71

Equivalent to coll[keys[0]][keys[1]]...[keys[n]] with safe fallback.

72

73

Parameters:

74

- keys: Sequence of keys for nested access

75

- coll: Nested mapping structure to access

76

- default: Value to return if path doesn't exist

77

- no_default: If True, raise KeyError instead of returning default

78

79

Returns:

80

Value at the nested path, or default if path doesn't exist

81

82

Raises:

83

KeyError: If path doesn't exist and no_default=True

84

"""

85

```

86

87

### Transformation Functions

88

89

Functions for use with the `.transform()` method of persistent collections to apply path-based modifications.

90

91

```python { .api }

92

def inc(x: int) -> int:

93

"""

94

Increment numeric value by 1.

95

96

Transformation function for use with .transform().

97

98

Parameters:

99

- x: Numeric value to increment

100

101

Returns:

102

x + 1

103

"""

104

105

def discard(evolver, key) -> None:

106

"""

107

Remove element from evolver during transformation.

108

109

Transformation function that removes a key/element from the

110

collection being transformed.

111

112

Parameters:

113

- evolver: Collection evolver (PMapEvolver, PVectorEvolver, PSetEvolver)

114

- key: Key/index/element to remove

115

"""

116

117

def rex(expr: str) -> callable:

118

"""

119

Create regex matcher for transformation paths.

120

121

Returns a function that tests if a string matches the regex pattern.

122

Useful for selecting which keys/paths to transform.

123

124

Parameters:

125

- expr: Regular expression pattern

126

127

Returns:

128

Function that tests strings against the regex

129

"""

130

131

def ny(_) -> bool:

132

"""

133

Matcher that always returns True.

134

135

Useful as a catch-all matcher in transformations when you want

136

to match any value.

137

138

Parameters:

139

- _: Any value (ignored)

140

141

Returns:

142

True (always)

143

"""

144

```

145

146

### Immutable Factory

147

148

Create namedtuple-like immutable classes with optional field validation.

149

150

```python { .api }

151

def immutable(

152

members: Union[str, Iterable[str]] = '',

153

name: str = 'Immutable',

154

verbose: bool = False

155

) -> type:

156

"""

157

Create an immutable namedtuple-like class.

158

159

Creates a class similar to collections.namedtuple but with additional

160

immutability guarantees and optional field validation.

161

162

Parameters:

163

- members: Field names as string (space/comma separated) or iterable

164

- name: Name for the created class

165

- verbose: If True, print the generated class definition

166

167

Returns:

168

Immutable class type with specified fields

169

"""

170

```

171

172

## Usage Examples

173

174

### Freeze/Thaw Conversions

175

176

```python

177

from pyrsistent import freeze, thaw, pmap, pvector

178

179

# Convert nested mutable structures to persistent

180

mutable_data = {

181

'users': [

182

{'name': 'Alice', 'tags': {'admin', 'active'}},

183

{'name': 'Bob', 'tags': {'user', 'active'}}

184

],

185

'config': {

186

'debug': True,

187

'features': ['auth', 'logging']

188

}

189

}

190

191

# Recursively convert to persistent structures

192

persistent_data = freeze(mutable_data)

193

# Result: pmap({

194

# 'users': pvector([

195

# pmap({'name': 'Alice', 'tags': pset(['admin', 'active'])}),

196

# pmap({'name': 'Bob', 'tags': pset(['user', 'active'])})

197

# ]),

198

# 'config': pmap({

199

# 'debug': True,

200

# 'features': pvector(['auth', 'logging'])

201

# })

202

# })

203

204

# Convert back to mutable for JSON serialization or external APIs

205

mutable_again = thaw(persistent_data)

206

import json

207

json_str = json.dumps(mutable_again)

208

```

209

210

### Mutant Decorator

211

212

```python

213

from pyrsistent import mutant, pmap

214

215

@mutant

216

def process_user_data(data):

217

"""

218

Function that expects and returns mutable data, but we want to

219

use persistent structures internally for safety.

220

"""

221

# data is automatically frozen (converted to persistent)

222

# Work with persistent data safely

223

if 'email' not in data:

224

data = data.set('email', '')

225

226

data = data.set('processed', True)

227

228

# Return value is automatically frozen

229

return data

230

231

# Use with mutable input - automatically converted

232

user_dict = {'name': 'Alice', 'age': 30}

233

result = process_user_data(user_dict)

234

# result is a pmap, input dict is unchanged

235

```

236

237

### Nested Access

238

239

```python

240

from pyrsistent import get_in, pmap, pvector

241

242

# Complex nested structure

243

data = pmap({

244

'api': pmap({

245

'v1': pmap({

246

'endpoints': pvector([

247

pmap({'path': '/users', 'methods': pvector(['GET', 'POST'])}),

248

pmap({'path': '/posts', 'methods': pvector(['GET', 'POST', 'DELETE'])})

249

])

250

})

251

}),

252

'config': pmap({

253

'database': pmap({

254

'host': 'localhost',

255

'port': 5432

256

})

257

})

258

})

259

260

# Safe nested access

261

db_host = get_in(['config', 'database', 'host'], data) # 'localhost'

262

api_endpoints = get_in(['api', 'v1', 'endpoints'], data) # pvector([...])

263

missing = get_in(['config', 'cache', 'ttl'], data, default=300) # 300

264

265

# Access with index for vectors

266

first_endpoint = get_in(['api', 'v1', 'endpoints', 0, 'path'], data) # '/users'

267

268

# Raise error if path doesn't exist

269

try:

270

get_in(['nonexistent', 'path'], data, no_default=True)

271

except KeyError:

272

print("Path not found")

273

```

274

275

### Transformations

276

277

```python

278

from pyrsistent import pmap, pvector, inc, discard, rex, ny

279

280

# Apply transformations to nested structures

281

data = pmap({

282

'counters': pmap({'page_views': 100, 'api_calls': 50}),

283

'users': pvector(['alice', 'bob', 'charlie']),

284

'temp_data': 'to_be_removed'

285

})

286

287

# Increment all counters

288

transformed = data.transform(

289

['counters', ny], inc # For any key in counters, apply inc function

290

)

291

# Result: counters become {'page_views': 101, 'api_calls': 51}

292

293

# Remove elements matching pattern

294

transformed2 = data.transform(

295

[rex(r'temp_.*')], discard # Remove any key matching temp_*

296

)

297

# Result: 'temp_data' key is removed

298

299

# Complex transformation combining multiple operations

300

def process_user(user):

301

return user.upper() if isinstance(user, str) else user

302

303

transformed3 = data.transform(

304

['users', ny], process_user, # Transform all users

305

['counters', 'page_views'], lambda x: x * 2, # Double page views

306

['temp_data'], discard # Remove temp data

307

)

308

```

309

310

### Immutable Classes

311

312

```python

313

from pyrsistent import immutable

314

315

# Create immutable point class

316

Point = immutable('x y', name='Point')

317

p1 = Point(x=1, y=2)

318

p2 = Point(3, 4) # Positional args also work

319

320

print(p1.x, p1.y) # 1 2

321

print(p1) # Point(x=1, y=2)

322

323

# Immutable - cannot modify

324

try:

325

p1.x = 5 # Raises AttributeError

326

except AttributeError:

327

print("Cannot modify immutable object")

328

329

# Create new instances with _replace

330

p3 = p1._replace(x=10) # Point(x=10, y=2)

331

332

# With more complex fields

333

Person = immutable('name age email', name='Person')

334

person = Person('Alice', 30, 'alice@example.com')

335

336

# Support for tuple unpacking

337

name, age, email = person

338

```

339

340

### Integration Patterns

341

342

```python

343

from pyrsistent import freeze, thaw, pmap

344

import json

345

346

# Working with JSON APIs

347

def load_config(filename):

348

"""Load configuration from JSON file into persistent structure."""

349

with open(filename) as f:

350

mutable_config = json.load(f)

351

return freeze(mutable_config)

352

353

def save_config(config, filename):

354

"""Save persistent configuration to JSON file."""

355

with open(filename, 'w') as f:

356

json.dump(thaw(config), f, indent=2)

357

358

# Thread-safe configuration management

359

class ConfigManager:

360

def __init__(self, initial_config):

361

self._config = freeze(initial_config)

362

363

def get_config(self):

364

return self._config # Safe to share between threads

365

366

def update_config(self, updates):

367

# Atomic update - no race conditions

368

self._config = self._config.update(freeze(updates))

369

370

def get_setting(self, *path):

371

return get_in(path, self._config)

372

373

# Usage

374

config_mgr = ConfigManager({'database': {'host': 'localhost', 'port': 5432}})

375

db_host = config_mgr.get_setting('database', 'host')

376

config_mgr.update_config({'database': {'timeout': 30}})

377

```