0
# Functional Programming
1
2
Functional programming utilities including curried functions, return value transformers, and functional composition tools for advanced data processing patterns.
3
4
## Capabilities
5
6
### Return Value Transformers
7
8
Transform function return values with decorators.
9
10
```python { .api }
11
def apply_to_return_value(callback):
12
"""
13
Decorator to apply callback to function return value.
14
15
Args:
16
callback: Function to apply to return value
17
18
Returns:
19
Decorator function
20
"""
21
22
def to_tuple(func):
23
"""Decorator to convert return value to tuple."""
24
25
def to_list(func):
26
"""Decorator to convert return value to list."""
27
28
def to_set(func):
29
"""Decorator to convert return value to set."""
30
31
def to_dict(func):
32
"""Decorator to convert return value to dict."""
33
34
def to_ordered_dict(func):
35
"""Decorator to convert return value to OrderedDict."""
36
37
def flatten_return(func):
38
"""Decorator to flatten returned iterables."""
39
40
def reversed_return(func):
41
"""Decorator to reverse returned sequences."""
42
43
def sort_return(func):
44
"""Decorator to sort returned sequences."""
45
```
46
47
### Curried Functions
48
49
Access curried versions of functions for functional composition.
50
51
```python { .api }
52
from eth_utils.curried import (
53
# Address functions
54
to_checksum_address, is_checksum_address,
55
56
# Conversion functions
57
to_bytes, to_hex, to_int, to_text,
58
59
# ABI functions
60
filter_abi_by_type, filter_abi_by_name,
61
62
# Formatting functions
63
apply_formatter_if, apply_formatters_to_dict,
64
65
# Currency functions
66
from_wei, to_wei,
67
68
# Other utilities
69
clamp, get_logger
70
)
71
```
72
73
## Usage Examples
74
75
### Return Value Transformation
76
77
```python
78
from eth_utils import to_list, to_tuple, flatten_return, sort_return
79
80
@to_list
81
def get_transaction_hashes():
82
"""Get transaction hashes as generator, return as list."""
83
for i in range(5):
84
yield f"0x{i:064x}"
85
86
@to_tuple
87
def get_address_components(address):
88
"""Split address into components, return as tuple."""
89
return [address[:2], address[2:10], address[10:]]
90
91
@flatten_return
92
def get_nested_data():
93
"""Return nested data, flattened."""
94
return [[1, 2], [3, 4], [5, 6]]
95
96
@sort_return
97
def get_unsorted_addresses():
98
"""Return addresses in sorted order."""
99
return [
100
"0xd3CdA913deB6f67967B99D67aCDFa1712C293601",
101
"0x742d35cc6634c0532925a3b8c17b1e8b4e1d1123",
102
"0xa1b2c3d4e5f6789012345678901234567890abcd"
103
]
104
105
# Usage
106
tx_hashes = get_transaction_hashes() # Returns list instead of generator
107
print(type(tx_hashes)) # <class 'list'>
108
109
components = get_address_components("0x742d35cc6634c0532925a3b8c17b1e8b4e1d1123")
110
print(components) # ('0x', '742d35cc', '6634c053...')
111
112
flat_data = get_nested_data() # Returns [1, 2, 3, 4, 5, 6]
113
sorted_addrs = get_unsorted_addresses() # Returns sorted list
114
```
115
116
### Curried Function Composition
117
118
```python
119
from eth_utils.curried import to_checksum_address, filter_abi_by_type, apply_formatter_if, is_string
120
from functools import partial
121
122
# Create specialized formatters using curried functions
123
format_address = apply_formatter_if(is_string, to_checksum_address)
124
get_functions = filter_abi_by_type("function")
125
get_events = filter_abi_by_type("event")
126
127
# Use in data processing pipeline
128
def process_contract_data(contract_abi, addresses):
129
"""Process contract ABI and addresses with functional style."""
130
131
# Extract functions and events using curried functions
132
functions = get_functions(contract_abi)
133
events = get_events(contract_abi)
134
135
# Format all addresses using curried formatter
136
formatted_addresses = [format_address(addr) for addr in addresses]
137
138
return {
139
"functions": functions,
140
"events": events,
141
"addresses": formatted_addresses
142
}
143
144
# Example usage
145
contract_abi = [
146
{"type": "function", "name": "transfer"},
147
{"type": "event", "name": "Transfer"}
148
]
149
150
addresses = [
151
"0xd3cda913deb6f67967b99d67acdfa1712c293601",
152
b'\x74\x2d\x35\xcc\x66\x34\xc0\x53\x29\x25\xa3\xb8\xc1\x7b\x1e\x8b\x4e\x1d\x11\x23'
153
]
154
155
result = process_contract_data(contract_abi, addresses)
156
print(f"Found {len(result['functions'])} functions")
157
print(f"Formatted addresses: {result['addresses']}")
158
```
159
160
### Functional Data Pipeline
161
162
```python
163
from eth_utils import apply_to_return_value, to_list, flatten_return
164
from eth_utils.curried import to_int, from_wei
165
166
def create_pipeline(*transformers):
167
"""Create a functional processing pipeline."""
168
def pipeline(data):
169
result = data
170
for transformer in transformers:
171
result = transformer(result)
172
return result
173
return pipeline
174
175
# Create specialized transformers
176
@to_list
177
def extract_values(transactions):
178
"""Extract values from transaction objects."""
179
for tx in transactions:
180
yield tx.get('value', '0x0')
181
182
@apply_to_return_value(lambda values: [to_int(hexstr=v) for v in values])
183
def convert_hex_to_int(hex_values):
184
"""Convert hex values to integers."""
185
return hex_values
186
187
@apply_to_return_value(lambda values: [from_wei(v, 'ether') for v in values])
188
def convert_wei_to_ether(wei_values):
189
"""Convert wei values to ether."""
190
return wei_values
191
192
# Create processing pipeline
193
process_transaction_values = create_pipeline(
194
extract_values,
195
convert_hex_to_int,
196
convert_wei_to_ether
197
)
198
199
# Example usage
200
transactions = [
201
{"hash": "0x123...", "value": "0xde0b6b3a7640000"}, # 1 ETH
202
{"hash": "0x456...", "value": "0x1bc16d674ec80000"}, # 2 ETH
203
{"hash": "0x789...", "value": "0x2386f26fc10000"} # 0.01 ETH
204
]
205
206
ether_values = process_transaction_values(transactions)
207
print(f"Transaction values in ETH: {ether_values}")
208
```
209
210
### Functional Utilities with Currying
211
212
```python
213
from eth_utils.curried import apply_formatters_to_dict, to_checksum_address, to_int
214
from functools import partial
215
216
# Create reusable formatters using curried functions
217
transaction_formatters = {
218
'to': to_checksum_address,
219
'from': to_checksum_address,
220
'value': partial(to_int, hexstr=None),
221
'gasPrice': partial(to_int, hexstr=None),
222
'gasLimit': partial(to_int, hexstr=None),
223
'nonce': partial(to_int, hexstr=None)
224
}
225
226
# Create specialized formatter
227
format_transaction = apply_formatters_to_dict(transaction_formatters)
228
229
def process_transaction_batch(raw_transactions):
230
"""Process batch of raw transactions functionally."""
231
return [dict(format_transaction(tx)) for tx in raw_transactions]
232
233
# Example usage
234
raw_transactions = [
235
{
236
'to': '0xd3cda913deb6f67967b99d67acdfa1712c293601',
237
'from': '0x742d35cc6634c0532925a3b8c17b1e8b4e1d1123',
238
'value': '0xde0b6b3a7640000',
239
'gasPrice': '0x4a817c800',
240
'gasLimit': '0x5208',
241
'nonce': '0x1'
242
}
243
]
244
245
formatted_transactions = process_transaction_batch(raw_transactions)
246
print(formatted_transactions[0]['to']) # Checksummed address
247
print(formatted_transactions[0]['value']) # Integer value
248
```
249
250
### Advanced Functional Patterns
251
252
```python
253
from eth_utils import apply_to_return_value, to_tuple, flatten_return
254
from eth_utils.curried import get_logger
255
from functools import wraps, reduce
256
import operator
257
258
def memoize(func):
259
"""Simple memoization decorator."""
260
cache = {}
261
262
@wraps(func)
263
def wrapper(*args, **kwargs):
264
key = str(args) + str(sorted(kwargs.items()))
265
if key not in cache:
266
cache[key] = func(*args, **kwargs)
267
return cache[key]
268
return wrapper
269
270
def compose(*functions):
271
"""Compose functions right to left."""
272
return reduce(lambda f, g: lambda x: f(g(x)), functions, lambda x: x)
273
274
def pipe(*functions):
275
"""Pipe functions left to right."""
276
return reduce(lambda f, g: lambda x: g(f(x)), functions, lambda x: x)
277
278
# Create functional processing chains
279
@memoize
280
@to_tuple
281
def expensive_calculation(data):
282
"""Expensive calculation with memoization."""
283
# Simulate expensive operation
284
return [x * 2 for x in data]
285
286
# Function composition example
287
from eth_utils.curried import to_hex, keccak
288
289
# Compose functions: hash then encode
290
hash_and_encode = compose(to_hex, keccak)
291
292
# Use composed function
293
text_data = "Hello, Ethereum!"
294
result = hash_and_encode(text=text_data)
295
print(f"Hash and encoded: {result}")
296
297
# Pipeline example
298
process_data = pipe(
299
lambda x: x.upper(),
300
lambda x: x.replace(" ", "_"),
301
lambda x: f"processed_{x}"
302
)
303
304
processed = process_data("hello world")
305
print(processed) # processed_HELLO_WORLD
306
```
307
308
### Curried Utility Functions
309
310
```python
311
from eth_utils.curried import clamp, get_logger
312
from functools import partial
313
314
# Create specialized clamping functions
315
clamp_percentage = clamp(0, 100)
316
clamp_gas_price = clamp(1000000000, 100000000000) # 1-100 gwei
317
318
# Create specialized loggers
319
debug_logger = get_logger("debug")
320
error_logger = get_logger("error")
321
322
def validate_gas_price(gas_price):
323
"""Validate and clamp gas price."""
324
clamped = clamp_gas_price(gas_price)
325
326
if clamped != gas_price:
327
debug_logger.warning(f"Gas price clamped from {gas_price} to {clamped}")
328
329
return clamped
330
331
def validate_percentage(value):
332
"""Validate and clamp percentage value."""
333
return clamp_percentage(value)
334
335
# Examples
336
print(validate_gas_price(50000000000)) # Valid gas price
337
print(validate_gas_price(200000000000)) # Clamped to max
338
print(validate_percentage(150)) # Clamped to 100
339
```
340
341
### Functional Error Handling
342
343
```python
344
from eth_utils import apply_to_return_value
345
from functools import wraps
346
347
def safe_call(default_value=None):
348
"""Decorator for safe function calls with default return."""
349
def decorator(func):
350
@wraps(func)
351
def wrapper(*args, **kwargs):
352
try:
353
return func(*args, **kwargs)
354
except Exception as e:
355
print(f"Error in {func.__name__}: {e}")
356
return default_value
357
return wrapper
358
return decorator
359
360
def maybe(func):
361
"""Maybe monad-like decorator."""
362
@wraps(func)
363
def wrapper(value):
364
if value is None:
365
return None
366
try:
367
return func(value)
368
except Exception:
369
return None
370
return wrapper
371
372
# Usage with functional patterns
373
@safe_call(default_value=[])
374
@apply_to_return_value(list)
375
def safe_process_data(data):
376
"""Safely process data with default return."""
377
if not data:
378
raise ValueError("No data provided")
379
380
for item in data:
381
yield item * 2
382
383
@maybe
384
def safe_to_checksum(address):
385
"""Safely convert to checksum address."""
386
from eth_utils import to_checksum_address
387
return to_checksum_address(address)
388
389
# Examples
390
result1 = safe_process_data([1, 2, 3]) # [2, 4, 6]
391
result2 = safe_process_data(None) # [] (default)
392
393
addr1 = safe_to_checksum("0x742d35cc6634c0532925a3b8c17b1e8b4e1d1123") # Checksummed
394
addr2 = safe_to_checksum("invalid") # None
395
addr3 = safe_to_checksum(None) # None
396
```
397
398
## Functional Programming Patterns
399
400
### Map, Filter, Reduce with eth-utils
401
402
```python
403
from eth_utils.curried import to_checksum_address, is_checksum_address, filter_abi_by_type
404
from functools import reduce
405
406
def functional_processing_example(addresses, contract_abi):
407
"""Example of functional processing with eth-utils."""
408
409
# Map: Convert all addresses to checksum format
410
checksum_addresses = list(map(to_checksum_address, addresses))
411
412
# Filter: Keep only valid checksum addresses
413
valid_addresses = list(filter(is_checksum_address, checksum_addresses))
414
415
# Reduce: Count total character length
416
total_length = reduce(lambda acc, addr: acc + len(addr), valid_addresses, 0)
417
418
# Extract functions using curried ABI filter
419
functions = filter_abi_by_type("function", contract_abi)
420
421
return {
422
"original_count": len(addresses),
423
"valid_count": len(valid_addresses),
424
"total_length": total_length,
425
"function_count": len(functions)
426
}
427
428
# Example usage
429
addresses = [
430
"0xd3cda913deb6f67967b99d67acdfa1712c293601",
431
"0x742d35cc6634c0532925a3b8c17b1e8b4e1d1123",
432
"invalid_address"
433
]
434
435
abi = [
436
{"type": "function", "name": "transfer"},
437
{"type": "function", "name": "approve"},
438
{"type": "event", "name": "Transfer"}
439
]
440
441
result = functional_processing_example(addresses, abi)
442
print(f"Processed {result['original_count']} addresses")
443
print(f"Found {result['valid_count']} valid addresses")
444
print(f"Found {result['function_count']} functions")
445
```