0
# Point-free Operations
1
2
Point-free style functions that enable composition without explicitly naming intermediate values, supporting functional programming idioms and container manipulation through higher-order functions.
3
4
## Capabilities
5
6
### Core Point-free Functions
7
8
Essential point-free operations that work with all container types for monadic composition.
9
10
```python { .api }
11
def bind(func: Callable[[T], Container[U]]) -> Callable[[Container[T]], Container[U]]:
12
"""Monadic bind operation (flatMap)"""
13
14
def map_(func: Callable[[T], U]) -> Callable[[Container[T]], Container[U]]:
15
"""Transform successful/present values"""
16
17
def apply(wrapped_func: Container[Callable[[T], U]]) -> Callable[[Container[T]], Container[U]]:
18
"""Apply wrapped function to wrapped value"""
19
20
def alt(func: Callable[[E], F]) -> Callable[[Container[T, E]], Container[T, F]]:
21
"""Transform error values"""
22
23
def lash(func: Callable[[E], Container[T, F]]) -> Callable[[Container[T, E]], Container[T, F]]:
24
"""Handle error cases with recovery"""
25
26
def swap() -> Callable[[Container[T, E]], Container[E, T]]:
27
"""Swap success and failure values"""
28
```
29
30
Usage examples:
31
32
```python
33
from returns.pointfree import bind, map_, alt, lash
34
from returns.result import Success, Failure, Result
35
from returns.pipeline import flow
36
37
# Point-free bind
38
def double(x: int) -> Result[int, str]:
39
return Success(x * 2)
40
41
def validate_positive(x: int) -> Result[int, str]:
42
return Success(x) if x > 0 else Failure("Must be positive")
43
44
# Traditional style
45
result = Success(5).bind(double).bind(validate_positive)
46
47
# Point-free style
48
pipeline = flow(
49
Success(5),
50
bind(double),
51
bind(validate_positive)
52
)
53
54
# Point-free map
55
def add_one(x: int) -> int:
56
return x + 1
57
58
transform = map_(add_one)
59
result = transform(Success(42)) # Success(43)
60
61
# Point-free alt for error transformation
62
def format_error(error: str) -> str:
63
return f"Error: {error}"
64
65
handle_error = alt(format_error)
66
result = handle_error(Failure("invalid input")) # Failure("Error: invalid input")
67
```
68
69
### Specialized Bind Operations
70
71
Type-specific bind operations for different container combinations.
72
73
```python { .api }
74
def bind_result(func: Callable[[T], Result[U, E]]) -> Callable[[Container[T]], Container[U, E]]:
75
"""Bind operation specialized for Result"""
76
77
def bind_io(func: Callable[[T], IO[U]]) -> Callable[[Container[T]], Container[U]]:
78
"""Bind operation specialized for IO"""
79
80
def bind_future(func: Callable[[T], Future[U]]) -> Callable[[Container[T]], Container[U]]:
81
"""Bind operation specialized for Future"""
82
83
def bind_async(func: Callable[[T], Awaitable[U]]) -> Callable[[Container[T]], Awaitable[Container[U]]]:
84
"""Bind operation for async functions"""
85
86
def bind_context(func: Callable[[T], RequiresContext[U, Deps]]) -> Callable[[RequiresContext[T, Deps]], RequiresContext[U, Deps]]:
87
"""Bind operation for context-dependent computations"""
88
```
89
90
Usage examples:
91
92
```python
93
from returns.pointfree import bind_result, bind_io, bind_context
94
from returns.result import Success, safe
95
from returns.io import IO
96
from returns.context import RequiresContext
97
98
# Specialized Result bind
99
@safe
100
def parse_int(s: str) -> int:
101
return int(s)
102
103
@safe
104
def divide_by_two(x: int) -> float:
105
return x / 2
106
107
process_string = flow(
108
Success("42"),
109
bind_result(parse_int),
110
bind_result(divide_by_two)
111
) # Success(21.0)
112
113
# IO bind
114
def read_file(path: str) -> IO[str]:
115
return IO(lambda: open(path).read())
116
117
def process_content(content: str) -> IO[int]:
118
return IO(lambda: len(content))
119
120
process_file = flow(
121
IO(lambda: "data.txt"),
122
bind_io(read_file),
123
bind_io(process_content)
124
)
125
126
# Context bind
127
def get_config() -> RequiresContext[dict, dict]:
128
return RequiresContext(lambda env: env["config"])
129
130
def get_database_url(config: dict) -> RequiresContext[str, dict]:
131
return RequiresContext(lambda env: config["db_url"])
132
133
get_db_url = flow(
134
RequiresContext.ask(),
135
bind_context(get_config),
136
bind_context(get_database_url)
137
)
138
```
139
140
### Context Manipulation
141
142
Functions for working with context-dependent computations.
143
144
```python { .api }
145
def modify_env(func: Callable[[Deps], NewDeps]) -> Callable[[RequiresContext[T, Deps]], RequiresContext[T, NewDeps]]:
146
"""Modify the environment/context"""
147
148
def local(func: Callable[[Deps], NewDeps]) -> Callable[[RequiresContext[T, NewDeps]], RequiresContext[T, Deps]]:
149
"""Run computation with modified local context"""
150
```
151
152
Usage example:
153
154
```python
155
from returns.pointfree import modify_env
156
from returns.context import RequiresContext
157
158
class Config:
159
def __init__(self, debug: bool, db_url: str):
160
self.debug = debug
161
self.db_url = db_url
162
163
def enable_debug(config: Config) -> Config:
164
return Config(True, config.db_url)
165
166
def get_db_connection() -> RequiresContext[str, Config]:
167
return RequiresContext(lambda cfg: f"Connected to {cfg.db_url} (debug={cfg.debug})")
168
169
# Modify context before operation
170
debug_connection = flow(
171
get_db_connection(),
172
modify_env(enable_debug)
173
)
174
175
config = Config(False, "postgresql://localhost")
176
result = debug_connection(config) # "Connected to postgresql://localhost (debug=True)"
177
```
178
179
### Composition Utilities
180
181
Advanced composition functions for complex pipelines.
182
183
```python { .api }
184
def unify(container: Container[T]) -> Container[T]:
185
"""Unify container types for composition"""
186
187
def compose_result(*functions: Callable) -> Callable:
188
"""Compose functions that return Results"""
189
190
def rescue(func: Callable[[E], Container[T, F]]) -> Callable[[Container[T, E]], Container[T, F]]:
191
"""Recover from errors with alternative computation"""
192
```
193
194
Usage examples:
195
196
```python
197
from returns.pointfree import compose_result, rescue
198
from returns.result import Success, Failure, safe
199
200
# Compose Result-returning functions
201
@safe
202
def parse_int(s: str) -> int:
203
return int(s)
204
205
@safe
206
def square(x: int) -> int:
207
return x * x
208
209
@safe
210
def to_string(x: int) -> str:
211
return str(x)
212
213
process_pipeline = compose_result(parse_int, square, to_string)
214
result = process_pipeline("5") # Success("25")
215
216
# Error recovery
217
def handle_parse_error(error: Exception) -> Result[int, str]:
218
return Success(0) if "invalid literal" in str(error) else Failure("Unexpected error")
219
220
safe_parse = flow(
221
safe(int)("invalid"),
222
rescue(handle_parse_error)
223
) # Success(0)
224
```
225
226
### Container-specific Operations
227
228
Specialized operations for specific container types.
229
230
```python { .api }
231
# Maybe-specific
232
def or_else(default: Maybe[T]) -> Callable[[Maybe[T]], Maybe[T]]:
233
"""Provide alternative Maybe if Nothing"""
234
235
def or_else_call(func: Callable[[], Maybe[T]]) -> Callable[[Maybe[T]], Maybe[T]]:
236
"""Provide alternative Maybe from function if Nothing"""
237
238
# Result-specific
239
def value_or(default: T) -> Callable[[Result[T, E]], T]:
240
"""Extract value or return default"""
241
242
def unwrap_or(default: T) -> Callable[[Container[T]], T]:
243
"""Unwrap container value or return default"""
244
245
# IO-specific
246
def unsafe_perform_io() -> Callable[[IO[T]], T]:
247
"""Execute IO operation (unsafe)"""
248
```
249
250
Usage examples:
251
252
```python
253
from returns.pointfree import or_else, value_or, unwrap_or
254
from returns.maybe import Some, Nothing
255
from returns.result import Success, Failure
256
257
# Maybe alternatives
258
maybe_value = Nothing
259
default_some = Some("default")
260
261
result = or_else(default_some)(maybe_value) # Some("default")
262
263
# Result value extraction
264
success_result = Success(42)
265
failure_result = Failure("error")
266
267
get_value = value_or(0)
268
value1 = get_value(success_result) # 42
269
value2 = get_value(failure_result) # 0
270
271
# Universal unwrap
272
unwrap_safe = unwrap_or("fallback")
273
value3 = unwrap_safe(Success("hello")) # "hello"
274
value4 = unwrap_safe(Failure("error")) # "fallback"
275
```
276
277
## Point-free Composition Patterns
278
279
### Pipeline Construction
280
281
```python
282
from returns.pointfree import bind, map_, alt
283
from returns.pipeline import flow
284
from returns.result import Success, safe
285
286
# Build processing pipeline
287
@safe
288
def parse_float(s: str) -> float:
289
return float(s)
290
291
def format_currency(amount: float) -> str:
292
return f"${amount:.2f}"
293
294
def handle_error(error: Exception) -> str:
295
return "Invalid amount"
296
297
process_amount = flow(
298
bind(parse_float),
299
map_(format_currency),
300
alt(lambda _: "Invalid amount")
301
)
302
303
result = process_amount(Success("123.456")) # Success("$123.46")
304
```
305
306
### Function Composition
307
308
```python
309
from returns.pointfree import bind, map_
310
from returns.curry import curry
311
312
@curry
313
def add(a: int, b: int) -> int:
314
return a + b
315
316
@curry
317
def multiply(a: int, b: int) -> int:
318
return a * b
319
320
# Point-free arithmetic pipeline
321
add_five = add(5)
322
double = multiply(2)
323
324
arithmetic = flow(
325
map_(add_five),
326
map_(double)
327
)
328
329
result = arithmetic(Success(10)) # Success(30)
330
```
331
332
### Error Handling Pipeline
333
334
```python
335
from returns.pointfree import lash, alt, rescue
336
from returns.result import Failure, Success
337
338
def retry_once(error: str) -> Result[int, str]:
339
return Success(42) if "retry" in error else Failure("Failed permanently")
340
341
def log_error(error: str) -> str:
342
print(f"Error occurred: {error}")
343
return error
344
345
error_pipeline = flow(
346
lash(retry_once), # Try to recover
347
alt(log_error) # Log if still failing
348
)
349
350
result = error_pipeline(Failure("retry please")) # Success(42)
351
```
352
353
Point-free operations enable clean, composable functional programming by removing the need to explicitly name intermediate values, leading to more readable and maintainable code pipelines.