or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

curried.mddicttoolz.mdfunctoolz.mdindex.mditertoolz.mdsandbox.md

functoolz.mddocs/

0

# Function Composition

1

2

Higher-order functions for composing, currying, and transforming functions. These utilities enable elegant functional programming patterns and pipeline creation with support for memoization, partial application, and function introspection.

3

4

## Capabilities

5

6

### Basic Function Operations

7

8

Core utilities for working with functions and applying them in various ways.

9

10

```python { .api }

11

def identity(x):

12

"""

13

Identity function - returns input unchanged.

14

15

Parameters:

16

- x: any value

17

18

Returns:

19

The input value x unchanged

20

"""

21

22

def apply(*func_and_args, **kwargs):

23

"""

24

Apply function to arguments and return result.

25

26

Parameters:

27

- func_and_args: function followed by its arguments

28

- **kwargs: keyword arguments for function

29

30

Returns:

31

Result of calling func(*args, **kwargs)

32

"""

33

34

def do(func, x):

35

"""

36

Run function on x for side effects, return x unchanged.

37

38

Parameters:

39

- func: function to call for side effects

40

- x: value to pass to function and return

41

42

Returns:

43

The input value x (func result is discarded)

44

"""

45

46

def flip(func, a, b):

47

"""

48

Call function with arguments flipped.

49

50

Parameters:

51

- func: binary function to call

52

- a: first argument (becomes second)

53

- b: second argument (becomes first)

54

55

Returns:

56

Result of func(b, a)

57

"""

58

```

59

60

### Function Composition

61

62

Functions for combining multiple functions into pipelines and compositions.

63

64

```python { .api }

65

def compose(*funcs):

66

"""

67

Compose functions to operate in series (right to left).

68

69

Parameters:

70

- *funcs: functions to compose

71

72

Returns:

73

Function that applies all input functions in reverse order

74

"""

75

76

def compose_left(*funcs):

77

"""

78

Compose functions left to right.

79

80

Parameters:

81

- *funcs: functions to compose

82

83

Returns:

84

Function that applies all input functions in given order

85

"""

86

87

def pipe(data, *funcs):

88

"""

89

Pipe value through sequence of functions (left to right).

90

91

Parameters:

92

- data: initial value

93

- *funcs: functions to apply in sequence

94

95

Returns:

96

Result of applying all functions to data in sequence

97

"""

98

99

def thread_first(val, *forms):

100

"""

101

Thread value through sequence of functions (value as first argument).

102

103

Parameters:

104

- val: value to thread through functions

105

- *forms: functions or (function, *args) tuples

106

107

Returns:

108

Result of threading val through all forms as first argument

109

"""

110

111

def thread_last(val, *forms):

112

"""

113

Thread value through sequence of functions (value as last argument).

114

115

Parameters:

116

- val: value to thread through functions

117

- *forms: functions or (function, *args) tuples

118

119

Returns:

120

Result of threading val through all forms as last argument

121

"""

122

```

123

124

### Function Modification

125

126

Functions that modify or enhance the behavior of other functions.

127

128

```python { .api }

129

def complement(func):

130

"""

131

Convert predicate function to its logical complement.

132

133

Parameters:

134

- func: predicate function returning True/False

135

136

Returns:

137

Function that returns opposite boolean result

138

"""

139

140

def juxt(*funcs):

141

"""

142

Create function that calls several functions with same arguments.

143

144

Parameters:

145

- *funcs: functions to call with same arguments

146

147

Returns:

148

Function that returns tuple of results from all input functions

149

"""

150

151

def memoize(func, cache=None, key=None):

152

"""

153

Cache function results for faster repeated evaluation.

154

155

Parameters:

156

- func: function to memoize

157

- cache: dictionary to use for caching (optional)

158

- key: function to compute cache key (optional)

159

160

Returns:

161

Memoized version of input function

162

"""

163

164

def excepts(exc, func, handler=return_none):

165

"""

166

Create function with functional try/except block.

167

168

Parameters:

169

- exc: exception type or tuple of types to catch

170

- func: function to wrap with exception handling

171

- handler: function to call on exception (default returns None)

172

173

Returns:

174

Function that catches specified exceptions and calls handler

175

"""

176

177

def return_none(exc):

178

"""

179

Returns None regardless of input.

180

181

Used as default exception handler for excepts function.

182

183

Parameters:

184

- exc: exception (ignored)

185

186

Returns:

187

None

188

"""

189

```

190

191

### Currying & Partial Application

192

193

Support for currying and partial application of functions.

194

195

```python { .api }

196

class curry:

197

"""

198

Curry a callable for partial application.

199

200

A curried function can be called with fewer arguments than required,

201

returning a new function that expects the remaining arguments.

202

"""

203

204

def __init__(self, *args, **kwargs):

205

"""

206

Create curried function.

207

208

Parameters:

209

- func: function to curry

210

- *args: optional initial arguments

211

- **kwargs: optional initial keyword arguments

212

"""

213

214

def bind(self, *args, **kwargs):

215

"""

216

Create new curry with additional bound arguments.

217

218

Parameters:

219

- *args: additional positional arguments to bind

220

- **kwargs: additional keyword arguments to bind

221

222

Returns:

223

New curry instance with additional bound arguments

224

"""

225

226

def call(self, *args, **kwargs):

227

"""

228

Call function without currying (bypass curry behavior).

229

230

Parameters:

231

- *args: all positional arguments

232

- **kwargs: all keyword arguments

233

234

Returns:

235

Direct result of calling underlying function

236

"""

237

```

238

239

### Function Introspection

240

241

Utilities for inspecting function signatures and argument requirements.

242

243

```python { .api }

244

def num_required_args(func, sigspec=None):

245

"""

246

Number of required positional arguments for function.

247

248

Parameters:

249

- func: function to inspect

250

- sigspec: cached signature specification (optional)

251

252

Returns:

253

Integer number of required positional arguments

254

"""

255

256

def has_varargs(func, sigspec=None):

257

"""

258

Check if function accepts variable positional arguments (*args).

259

260

Parameters:

261

- func: function to inspect

262

- sigspec: cached signature specification (optional)

263

264

Returns:

265

True if function accepts *args

266

"""

267

268

def has_keywords(func, sigspec=None):

269

"""

270

Check if function accepts keyword arguments (**kwargs).

271

272

Parameters:

273

- func: function to inspect

274

- sigspec: cached signature specification (optional)

275

276

Returns:

277

True if function accepts **kwargs

278

"""

279

280

def is_valid_args(func, args, kwargs, sigspec=None):

281

"""

282

Check if func(*args, **kwargs) is valid call.

283

284

Parameters:

285

- func: function to check

286

- args: positional arguments tuple

287

- kwargs: keyword arguments dict

288

- sigspec: cached signature specification (optional)

289

290

Returns:

291

True if arguments are valid for function

292

"""

293

294

def is_partial_args(func, args, kwargs, sigspec=None):

295

"""

296

Check if partial(func, *args, **kwargs) could be valid.

297

298

Parameters:

299

- func: function to check

300

- args: positional arguments tuple

301

- kwargs: keyword arguments dict

302

- sigspec: cached signature specification (optional)

303

304

Returns:

305

True if partial application could be valid

306

"""

307

308

def is_arity(n, func, sigspec=None):

309

"""

310

Check if function takes exactly n positional arguments.

311

312

Parameters:

313

- n: expected number of arguments

314

- func: function to check

315

- sigspec: cached signature specification (optional)

316

317

Returns:

318

True if function requires exactly n positional arguments

319

"""

320

```

321

322

## Classes

323

324

```python { .api }

325

class Compose:

326

"""Function composition class for multiple function pipeline."""

327

328

def __init__(self, funcs):

329

"""

330

Create composition of functions.

331

332

Parameters:

333

- funcs: sequence of functions to compose

334

"""

335

336

def __call__(self, *args, **kwargs):

337

"""

338

Call composed functions on arguments.

339

340

Parameters:

341

- *args: positional arguments for first function

342

- **kwargs: keyword arguments for first function

343

344

Returns:

345

Result of applying all composed functions

346

"""

347

348

class InstanceProperty:

349

"""Property that returns different value when accessed on class vs instance."""

350

351

def __init__(self, fget=None, fset=None, fdel=None, doc=None, classval=None):

352

"""

353

Create instance property.

354

355

Parameters:

356

- fget: getter function

357

- fset: setter function (optional)

358

- fdel: deleter function (optional)

359

- doc: docstring (optional)

360

- classval: value returned when accessed on class (optional)

361

"""

362

```

363

364

## Usage Examples

365

366

### Function Pipeline Creation

367

368

```python

369

from toolz import pipe, compose, curry

370

371

# Data processing pipeline

372

def clean_text(text):

373

return text.strip().lower()

374

375

def remove_punctuation(text):

376

return ''.join(c for c in text if c.isalnum() or c.isspace())

377

378

def extract_words(text):

379

return text.split()

380

381

# Using pipe (left to right)

382

text = " Hello, World! "

383

words = pipe(

384

text,

385

clean_text,

386

remove_punctuation,

387

extract_words

388

)

389

# ['hello', 'world']

390

391

# Using compose (right to left)

392

text_processor = compose(

393

extract_words,

394

remove_punctuation,

395

clean_text

396

)

397

words = text_processor(" Hello, World! ")

398

# ['hello', 'world']

399

```

400

401

### Currying and Partial Application

402

403

```python

404

from toolz import curry

405

from operator import add, mul

406

407

# Create curried functions

408

@curry

409

def multiply(x, y):

410

return x * y

411

412

@curry

413

def power(base, exponent):

414

return base ** exponent

415

416

# Partial application

417

double = multiply(2)

418

square = power(exponent=2)

419

cube = power(exponent=3)

420

421

# Use in data processing

422

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

423

doubled = list(map(double, numbers)) # [2, 4, 6, 8, 10]

424

squared = list(map(square, numbers)) # [1, 4, 9, 16, 25]

425

cubed = list(map(cube, numbers)) # [1, 8, 27, 64, 125]

426

```

427

428

### Threading Operations

429

430

```python

431

from toolz import thread_first, thread_last

432

433

data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

434

435

# Thread first (data as first argument)

436

result = thread_first(

437

data,

438

(filter, lambda x: x % 2 == 0), # filter(lambda x: x % 2 == 0, data)

439

(map, lambda x: x * 2), # map(lambda x: x * 2, filtered)

440

list # list(mapped)

441

)

442

# [4, 8, 12, 16, 20]

443

444

# Thread last (data as last argument)

445

result = thread_last(

446

data,

447

(lambda seq: filter(lambda x: x % 2 == 0, seq)),

448

(lambda seq: map(lambda x: x * 2, seq)),

449

list

450

)

451

# [4, 8, 12, 16, 20]

452

```

453

454

### Function Composition with Memoization

455

456

```python

457

from toolz import memoize, compose

458

import time

459

460

@memoize

461

def expensive_calculation(n):

462

time.sleep(0.1) # Simulate expensive operation

463

return n * n

464

465

@memoize

466

def another_expensive_calc(n):

467

time.sleep(0.1)

468

return n + 10

469

470

# Compose memoized functions

471

pipeline = compose(another_expensive_calc, expensive_calculation)

472

473

# First call is slow

474

start = time.time()

475

result1 = pipeline(5) # expensive_calculation(5) -> another_expensive_calc(25)

476

time1 = time.time() - start

477

478

# Second call is fast (memoized)

479

start = time.time()

480

result2 = pipeline(5) # Both results cached

481

time2 = time.time() - start

482

483

print(f"First call: {time1:.2f}s, Second call: {time2:.4f}s")

484

# First call: 0.20s, Second call: 0.0001s

485

```

486

487

### Function Utilities

488

489

```python

490

from toolz import juxt, complement, do

491

492

# juxt - call multiple functions with same arguments

493

def add_one(x): return x + 1

494

def multiply_two(x): return x * 2

495

def square(x): return x * x

496

497

multi_transform = juxt(add_one, multiply_two, square)

498

result = multi_transform(5) # (6, 10, 25)

499

500

# complement - logical opposite

501

def is_even(x): return x % 2 == 0

502

is_odd = complement(is_even)

503

504

evens = list(filter(is_even, [1, 2, 3, 4, 5])) # [2, 4]

505

odds = list(filter(is_odd, [1, 2, 3, 4, 5])) # [1, 3, 5]

506

507

# do - side effects while passing through

508

def log_value(x):

509

print(f"Processing: {x}")

510

511

process_with_logging = lambda x: do(log_value, x * 2)

512

result = process_with_logging(5) # Prints "Processing: 10", returns 10

513

```