wird 1.0.0__tar.gz → 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
wird-1.2.0/PKG-INFO ADDED
@@ -0,0 +1,342 @@
1
+ Metadata-Version: 2.4
2
+ Name: wird
3
+ Version: 1.2.0
4
+ Summary: Basic monads for implementing pipe-styled processing
5
+ Project-URL: Repository, https://github.com/katunilya/wird
6
+ Project-URL: Issues, https://github.com/katunilya/wird/issues
7
+ Author-email: Ilya Katun <katun.ilya@gmail.com>
8
+ Maintainer-email: Ilya Katun <katun.ilya@gmail.com>
9
+ Requires-Python: >=3.13
10
+ Description-Content-Type: text/markdown
11
+
12
+ # wird
13
+
14
+ `wird` is a library that provides basic monads in python. Core idea is to provide
15
+ mechanics for writing purely python pipeline-styled code.
16
+
17
+ > **Why wird?** Wird is a misspelling of Anglo-Saxon / Old North word "wyrd". It means
18
+ > fate, but not totally predefined, more like a consequence of previous deeds.
19
+
20
+ ## Pipelines
21
+
22
+ Before getting into `wird` API it's worth explaining concept of pipeline-styled code.
23
+ Mainly our code is imperative - we describe what we do to achieve some result in steps,
24
+ one by one. It's not worth to reject imperative code in favor of declarative one (where
25
+ we describe the result instead of steps for getting it), as most languages are generally
26
+ imperative and it's more convenient to provide better ways to write it.
27
+
28
+ Different languages provide pipelines in different forms. For example in C# or Java it
29
+ is provided with so called Fluent API (sometimes method chaining). Example:
30
+
31
+ ```csharp
32
+ int[] numbers = [ 5, 10, 8, 3, 6, 12 ];
33
+
34
+ IEnumerable<int> evenNumbersSorted = numbers
35
+ .Where(num => num % 2 == 0)
36
+ .OrderBy(num => num);
37
+ ```
38
+
39
+ There we write some class that allows us to chain method execution in order to perform
40
+ some action. This is quite nice approach, however it's not really extensible and does
41
+ not suit to most of the business cases where we want to separate bits of logic into
42
+ different entities.
43
+
44
+ Mostly this kind of syntax is used for builder pattern:
45
+
46
+ ```csharp
47
+ var host = new WebHostBuilder()
48
+ .UseKestrel()
49
+ .UseContentRoot(Directory.GetCurrentDirectory())
50
+ .UseStartup<Startup>()
51
+ .Build();
52
+
53
+ host.Run();
54
+ ```
55
+
56
+ In functional languages you can find so called "pipe operator" - `|>`. Let's take a look
57
+ at simple case - we want to put to square some number, that convert that to string and
58
+ reverse it. In F# you might write that like:
59
+
60
+ ```fsharp
61
+ let result = rev (toStr (square 512))
62
+ ```
63
+
64
+ Problem of this piece of code is that despite or algorithm is simple and direct, when we
65
+ write code it steps are written in reverse order and we need to "unwrap" function calls.
66
+
67
+ With pipe operator same code becomes much more elegant:
68
+
69
+ ```fsharp
70
+ let result = 512
71
+ |> square
72
+ |> toStr
73
+ |> rev
74
+ ```
75
+
76
+ All actions are written one-by-one in the same order as they executed. This is much more
77
+ readable code.
78
+
79
+ Basically `wird` is written to provide this mechanic to python language in some
80
+ opinionated form inspired by Rust language.
81
+
82
+ ## Monads
83
+
84
+ ### `Value`
85
+
86
+ Container for sync value that provides pipe-styled execution of arbitrary functions.
87
+ Let's look at the example:
88
+
89
+ ```python
90
+ import operator
91
+
92
+ from wird import Value
93
+
94
+
95
+ res = (
96
+ Value(3)
97
+ .map(operator.add, 1) # 3 + 1 -> 4
98
+ .map(operator.mul, 3) # 4 * 3 -> 12
99
+ .map(operator.truediv, 2) # 12 / 2 -> 6
100
+ .inspect(print) # print 6.0 & pass next
101
+ .unwrap(as_type=int) # extract 6.0 from container
102
+ )
103
+ ```
104
+
105
+ `Value` is a simple wrapper around passed value with special methods (`map` /
106
+ `map_async` / `inspect` / `inspect_async`) that bind passed function to container value
107
+ (read as invoke / apply). Thus it is basically is a simplest monad.
108
+
109
+ `Value` provides the following interface:
110
+
111
+ - `Value.unwrap` - method for extracting internally stored value with optional type
112
+ casting (only for type checker, not actual casting happens)
113
+ - `Value.map` - binding method for sync functions
114
+ - `Value.map_async` - binding method for async functions
115
+ - `Value.inspect` - binding method for sync side-effect functions
116
+ - `Value.inspect_async` - binding method for async side-effect functions
117
+
118
+ Main different between `map` and `inspect` is that `map` wraps the result of the
119
+ executed function into `Value` container and `inspect` just invokes function passing
120
+ stored value next. If stored value is mutable, `inspect` can be used to modify it via
121
+ side effect.
122
+
123
+ ### `Future`
124
+
125
+ Container for async values. It is similar to `Value` and provides nearly the same
126
+ interface. When we invoke any of async methods in `Value` we actually return `Future`
127
+ container, as now stored value is computed asynchronously and requires `await`.
128
+
129
+ ```python
130
+ import operator
131
+
132
+ from wird import Value
133
+
134
+ async def mul_async(x: int, y: int) -> int:
135
+ return x * y
136
+
137
+ async def truediv_async(x: int, y: int) -> float:
138
+ return x / y
139
+
140
+ async def main():
141
+ res = await (
142
+ Value(3)
143
+ .map(operator.add, 1) # 3 + 1 -> 4 (Value)
144
+ .map_async(mul_async, 3) # 4 * 3 -> 12 (Future)
145
+ .map_async(truediv_async, 2) # 12 / 2 -> 6.0 (Future)
146
+ .inspect(print) # print 6.0 & pass next (Future)
147
+ .unwrap() # extract awaitable 6.0 from container
148
+ )
149
+
150
+ if __name__ == "__main__":
151
+ import asyncio
152
+ asyncio.run(main())
153
+ ```
154
+
155
+ `Future` provides the following interface:
156
+
157
+ - `Future.unwrap` - extract internally stored awaitable value
158
+ - `Future.map` - binding method for sync functions
159
+ - `Future.map_async` - binding method for async functions
160
+ - `Future.inspect` - binding method for sync side-effect functions
161
+ - `Future.inspect_async` - binding method for async side-effect functions
162
+ - `Future.from_` - static method for creating awaitable object from sync value
163
+
164
+ Also `Future` is awaitable by itself, so one can just await `Future` itself instead of
165
+ calling `Future.unwrap`, but to stay uniform it is recommended to use `Future.unwrap`.
166
+
167
+ ### `Maybe`
168
+
169
+ Despite `Value` and `Future`, `Maybe` is not a single container, but rather a pair of
170
+ containers - `Some` and `Empty`. Each resembles additional property of data - its
171
+ presence.
172
+
173
+ `Some` indicates that data is present allowing it to be processed. `Empty` on the other
174
+ hand marks that there is not data and we can't perform execution ignoring that.
175
+ Basically it hides explicit `is None` checks, taking it as internal rule of function
176
+ mapping.
177
+
178
+ Possible relevant case of usage is database patch / update operations, when we
179
+ intentionally want to provide some abstract interface that allows optional column
180
+ update. For example we store in SQL database following data structure:
181
+
182
+ ```python
183
+ from dataclasses import dataclass
184
+ from datetime import date
185
+
186
+
187
+ @dataclass
188
+ class Customer:
189
+ uid: int
190
+ first_name: str
191
+ second_name: str
192
+ birthdate: date | None = None
193
+ ```
194
+
195
+ We provide HTTP route to update this entity in DB. If we've provided a field in request
196
+ body, then this field must be updated. Commonly one will make each field in DTO (except
197
+ for ID) optional with default `None` value, but what to do with `birthdate`? When
198
+ parsing we will propagate default `None` so we do not know if this `None` was passed
199
+ explicitly or we've implicitly set it via DTO default.
200
+
201
+ `Maybe` allows to explicitly separate this cases, allowing us to have `None` as present value:
202
+
203
+ ```python
204
+ from dataclasses import dataclass
205
+ from datetime import date
206
+
207
+ from wird import Empty, Maybe
208
+
209
+
210
+ @dataclass
211
+ class CustomerUpdate:
212
+ uid: int
213
+ first_name: Maybe[str] = Empty()
214
+ second_name: Maybe[str] = Empty()
215
+ birthdate: Maybe[date | None] = Empty()
216
+ ```
217
+
218
+ Thus when `birthdate` is `Empty` we know that we do not have to update this column at
219
+ all, and when it is `Some` we can safely set `None` as desired value.
220
+
221
+ `Maybe` provides the following interface:
222
+
223
+ - `Maybe.unwrap` - extract internally stored value on `Some`, raise `EmptyUnwrapError`
224
+ on `Empty`
225
+ - `Maybe.unwrap_or` - extract internally stored value on `Some` or return passed
226
+ replacement value on `Empty`
227
+ - `Maybe.unwrap_or_none` - extract internally stored value on `Some` or return `None`
228
+ on `Empty`
229
+ - `Maybe.unwrap_or_else` - extract internally stored value on `Some` or return result of
230
+ execution of factory function for replacement value on `Empty`
231
+ - `Maybe.unwrap_or_else_async` - same as `Maybe.unwrap_or_else`, but for async factory
232
+ function
233
+ - `Maybe.map` - binding method for sync functions, applies only on `Some`
234
+ - `Maybe.map_async` - same as `Maybe.map`, but for async functions
235
+ - `Maybe.inspect` - binding method for sync side-effect functions, applies only on
236
+ `Some`
237
+ - `Maybe.inspect_async` - same as `Maybe.inspect`, but for async functions
238
+ - `Maybe.and_` - logical AND for 2 `Maybe` values, replaces self `Maybe` with passed
239
+ `Maybe` if first one is `Some`
240
+ - `Maybe.and_then` - same as `Maybe.map`, but for sync functions that return `Maybe`
241
+ - `Maybe.and_then_async` - same as `Maybe.and_then`, but for async functions
242
+ - `Maybe.or_` - logical OR for 2 `Maybe` values, replaces self `Maybe` with passed
243
+ `Maybe` if first one is `Empty`
244
+ - `Maybe.or_else` - replaces `Empty` with `Maybe` result of passed sync function
245
+ - `Maybe.or_else_async` - same as `Maybe.or_else`, but for async functions
246
+ - `Maybe.is_some` - `True` on `Some` container
247
+ - `Maybe.is_some_and` - `True` on `Some` container and passed predicate being `True`
248
+ - `Maybe.is_some_and_async` - same as `Maybe.is_some_and`, but for async predicates
249
+ - `Maybe.is_empty` - `True` on `Empty` container
250
+ - `Maybe.is_empty_or` - `True` on `Empty` container or passed predicate being `True`
251
+ - `Maybe.is_empty_or_async` - same as `Maybe.is_empty_or`, but for async predicates
252
+ - `Maybe.filter` - if predicate is `False` replaces `Maybe` with `Empty`
253
+ - `Maybe.filter_async` - same as `Maybe.filter`, but for async predicates
254
+
255
+ In order to provide seamless experience, instead of making developer to work with
256
+ `Future[Maybe[T]]` we provide `FutureMaybe` container that provides exactly the same
257
+ interface as sync `Maybe`. Worth noting that `FutureMaybe` is awaitable, like `Future`,
258
+ and returns internally stored `Maybe` instance.
259
+
260
+ Also in some cases one might need point-free versions of `Maybe` interface methods, so
261
+ one can access them via `maybe` module. For `FutureMaybe` point-free functions one can
262
+ use `future_maybe` module.
263
+
264
+ ### `Result`
265
+
266
+ Exception handling is one of the most important tasks in development. We often face
267
+ cases when invocation of some logic can lead to Exception raise. In python default
268
+ handling mechanism is `try` - `except` - `finally` block, which is actually just another
269
+ for of `if` statement.
270
+
271
+ Worst thing about this approach is that commonly in python the only way to know that
272
+ function can raise an exception is documentation (which is not always written or written
273
+ good). There is no explicit mechanism to tell LSP / type checker / linter, that this
274
+ specific function needs exception handling.
275
+
276
+ `Result` monad provides another approach, which is common for Rust and Go developers -
277
+ let's return exceptions instead of raising them. Thus we can explicitly tell that soma
278
+ action can fail and requires edge-case handling.
279
+
280
+ Like `Maybe`, `Result` is just a protocol and has 2 implementations:
281
+
282
+ - `Ok` - container indicating that calculation succeeded
283
+ - `Err` - container indicating that calculation failed
284
+
285
+ Simplest case of using `Result` is division:
286
+
287
+ ```python
288
+ from wird import Result, Ok, Err
289
+
290
+ def try_div(a: int, b: int) -> Result[float, ZeroDivisionError]:
291
+ if b == 0:
292
+ return Err(ZeroDivisionError())
293
+
294
+ return Ok(a / b)
295
+ ```
296
+
297
+ There we explicitly tell that division operation can lead to failure and even pinpoint
298
+ specific type of error.
299
+
300
+ `Result` provides the following interface:
301
+
302
+ - `Result.unwrap` - extract internally stored value of `Ok` or raise `ErrUnwrapError`
303
+ - `Result.unwrap_or` - extract internally stored value of `Ok` or return other
304
+ - `Result.unwrap_or_else` - extract internally stored value of `Ok` or return closure
305
+ result
306
+ - `Result.unwrap_or_else_async` - same as `Result.unwrap_or_else`, but for async
307
+ closures
308
+ - `Result.unwrap_err` - same as `Result.unwrap`, but for `Err`
309
+ - `Result.unwrap_err_or` - same as `Result.unwrap_err_or`, but for `Err`
310
+ - `Result.unwrap_err_or_else` - same as `Result.unwrap_err_or_else`, but for `Err`
311
+ - `Result.unwrap_err_or_else_async` - same as `Result.unwrap_err_or_else_async`, but for
312
+ `Err`
313
+ - `Result.map` - binding method for `Ok`
314
+ - `Result.map_async` - same as `Result.map`, but for async functions
315
+ - `Result.inspect` - binding side-effect method for `Ok`
316
+ - `Result.inspect_async`- same as `Result.inspect_async`, but for async functions
317
+ - `Result.map_err` - same as `Result.map`, but for `Err`
318
+ - `Result.map_err_async` - same as `Result.map_async`, but for `Err`
319
+ - `Result.inspect_err` - same as `Result.inspect`, but for `Err`
320
+ - `Result.inspect_err_async` - same as `Result.inspect_async`, but for `Err`
321
+ - `Result.and_` - logical AND, replaces current `Result` with passed on `Ok`
322
+ - `Result.and_then` - same as `Result.map`, but for functions returning `Result`
323
+ - `Result.and_then_async` - same as `Result.and_then`, but for async functions
324
+ - `Result.or_` - logical OR, replaces current `Result` with passed on `Err`
325
+ - `Result.or_else` - same as `Result.map_err`, but for functions returning `Result`
326
+ - `Result.or_else_async` - same as `Result.or_else`, but for async functions
327
+ - `Result.is_ok` - `True` on `Ok`
328
+ - `Result.is_ok_and` - `True` on `Ok` and predicate `True`
329
+ - `Result.is_ok_and_async` - same as `Result.is_ok_and`, but for async predicate
330
+ - `Result.is_ok_or` - `True` on `Ok` or `Err` predicate `True`
331
+ - `Result.is_ok_or_async` - same as `Result.is_ok_or`, but for async predicate
332
+ - `Result.is_err` - `True` on `Err`
333
+ - `Result.is_err_and` - `True` on `Err` and predicate `True`
334
+ - `Result.is_err_and_async` - same as `Result.is_err_and`, but for async predicate
335
+ - `Result.is_err_or` - `True` on `Err` or `Ok` predicate `True
336
+ - `Result.is_err_or_async` - same as `result.is_err_or`, but for async predicate
337
+
338
+ In the same manner as with `Maybe` we wird provides:
339
+
340
+ - `FutureResult` as seamless adapter for `Future[Result]`
341
+ - point-free `Result` API in `wird.result` module
342
+ - point-free `FutureResult` API in `wird.future_result` module
wird-1.2.0/README.md ADDED
@@ -0,0 +1,331 @@
1
+ # wird
2
+
3
+ `wird` is a library that provides basic monads in python. Core idea is to provide
4
+ mechanics for writing purely python pipeline-styled code.
5
+
6
+ > **Why wird?** Wird is a misspelling of Anglo-Saxon / Old North word "wyrd". It means
7
+ > fate, but not totally predefined, more like a consequence of previous deeds.
8
+
9
+ ## Pipelines
10
+
11
+ Before getting into `wird` API it's worth explaining concept of pipeline-styled code.
12
+ Mainly our code is imperative - we describe what we do to achieve some result in steps,
13
+ one by one. It's not worth to reject imperative code in favor of declarative one (where
14
+ we describe the result instead of steps for getting it), as most languages are generally
15
+ imperative and it's more convenient to provide better ways to write it.
16
+
17
+ Different languages provide pipelines in different forms. For example in C# or Java it
18
+ is provided with so called Fluent API (sometimes method chaining). Example:
19
+
20
+ ```csharp
21
+ int[] numbers = [ 5, 10, 8, 3, 6, 12 ];
22
+
23
+ IEnumerable<int> evenNumbersSorted = numbers
24
+ .Where(num => num % 2 == 0)
25
+ .OrderBy(num => num);
26
+ ```
27
+
28
+ There we write some class that allows us to chain method execution in order to perform
29
+ some action. This is quite nice approach, however it's not really extensible and does
30
+ not suit to most of the business cases where we want to separate bits of logic into
31
+ different entities.
32
+
33
+ Mostly this kind of syntax is used for builder pattern:
34
+
35
+ ```csharp
36
+ var host = new WebHostBuilder()
37
+ .UseKestrel()
38
+ .UseContentRoot(Directory.GetCurrentDirectory())
39
+ .UseStartup<Startup>()
40
+ .Build();
41
+
42
+ host.Run();
43
+ ```
44
+
45
+ In functional languages you can find so called "pipe operator" - `|>`. Let's take a look
46
+ at simple case - we want to put to square some number, that convert that to string and
47
+ reverse it. In F# you might write that like:
48
+
49
+ ```fsharp
50
+ let result = rev (toStr (square 512))
51
+ ```
52
+
53
+ Problem of this piece of code is that despite or algorithm is simple and direct, when we
54
+ write code it steps are written in reverse order and we need to "unwrap" function calls.
55
+
56
+ With pipe operator same code becomes much more elegant:
57
+
58
+ ```fsharp
59
+ let result = 512
60
+ |> square
61
+ |> toStr
62
+ |> rev
63
+ ```
64
+
65
+ All actions are written one-by-one in the same order as they executed. This is much more
66
+ readable code.
67
+
68
+ Basically `wird` is written to provide this mechanic to python language in some
69
+ opinionated form inspired by Rust language.
70
+
71
+ ## Monads
72
+
73
+ ### `Value`
74
+
75
+ Container for sync value that provides pipe-styled execution of arbitrary functions.
76
+ Let's look at the example:
77
+
78
+ ```python
79
+ import operator
80
+
81
+ from wird import Value
82
+
83
+
84
+ res = (
85
+ Value(3)
86
+ .map(operator.add, 1) # 3 + 1 -> 4
87
+ .map(operator.mul, 3) # 4 * 3 -> 12
88
+ .map(operator.truediv, 2) # 12 / 2 -> 6
89
+ .inspect(print) # print 6.0 & pass next
90
+ .unwrap(as_type=int) # extract 6.0 from container
91
+ )
92
+ ```
93
+
94
+ `Value` is a simple wrapper around passed value with special methods (`map` /
95
+ `map_async` / `inspect` / `inspect_async`) that bind passed function to container value
96
+ (read as invoke / apply). Thus it is basically is a simplest monad.
97
+
98
+ `Value` provides the following interface:
99
+
100
+ - `Value.unwrap` - method for extracting internally stored value with optional type
101
+ casting (only for type checker, not actual casting happens)
102
+ - `Value.map` - binding method for sync functions
103
+ - `Value.map_async` - binding method for async functions
104
+ - `Value.inspect` - binding method for sync side-effect functions
105
+ - `Value.inspect_async` - binding method for async side-effect functions
106
+
107
+ Main different between `map` and `inspect` is that `map` wraps the result of the
108
+ executed function into `Value` container and `inspect` just invokes function passing
109
+ stored value next. If stored value is mutable, `inspect` can be used to modify it via
110
+ side effect.
111
+
112
+ ### `Future`
113
+
114
+ Container for async values. It is similar to `Value` and provides nearly the same
115
+ interface. When we invoke any of async methods in `Value` we actually return `Future`
116
+ container, as now stored value is computed asynchronously and requires `await`.
117
+
118
+ ```python
119
+ import operator
120
+
121
+ from wird import Value
122
+
123
+ async def mul_async(x: int, y: int) -> int:
124
+ return x * y
125
+
126
+ async def truediv_async(x: int, y: int) -> float:
127
+ return x / y
128
+
129
+ async def main():
130
+ res = await (
131
+ Value(3)
132
+ .map(operator.add, 1) # 3 + 1 -> 4 (Value)
133
+ .map_async(mul_async, 3) # 4 * 3 -> 12 (Future)
134
+ .map_async(truediv_async, 2) # 12 / 2 -> 6.0 (Future)
135
+ .inspect(print) # print 6.0 & pass next (Future)
136
+ .unwrap() # extract awaitable 6.0 from container
137
+ )
138
+
139
+ if __name__ == "__main__":
140
+ import asyncio
141
+ asyncio.run(main())
142
+ ```
143
+
144
+ `Future` provides the following interface:
145
+
146
+ - `Future.unwrap` - extract internally stored awaitable value
147
+ - `Future.map` - binding method for sync functions
148
+ - `Future.map_async` - binding method for async functions
149
+ - `Future.inspect` - binding method for sync side-effect functions
150
+ - `Future.inspect_async` - binding method for async side-effect functions
151
+ - `Future.from_` - static method for creating awaitable object from sync value
152
+
153
+ Also `Future` is awaitable by itself, so one can just await `Future` itself instead of
154
+ calling `Future.unwrap`, but to stay uniform it is recommended to use `Future.unwrap`.
155
+
156
+ ### `Maybe`
157
+
158
+ Despite `Value` and `Future`, `Maybe` is not a single container, but rather a pair of
159
+ containers - `Some` and `Empty`. Each resembles additional property of data - its
160
+ presence.
161
+
162
+ `Some` indicates that data is present allowing it to be processed. `Empty` on the other
163
+ hand marks that there is not data and we can't perform execution ignoring that.
164
+ Basically it hides explicit `is None` checks, taking it as internal rule of function
165
+ mapping.
166
+
167
+ Possible relevant case of usage is database patch / update operations, when we
168
+ intentionally want to provide some abstract interface that allows optional column
169
+ update. For example we store in SQL database following data structure:
170
+
171
+ ```python
172
+ from dataclasses import dataclass
173
+ from datetime import date
174
+
175
+
176
+ @dataclass
177
+ class Customer:
178
+ uid: int
179
+ first_name: str
180
+ second_name: str
181
+ birthdate: date | None = None
182
+ ```
183
+
184
+ We provide HTTP route to update this entity in DB. If we've provided a field in request
185
+ body, then this field must be updated. Commonly one will make each field in DTO (except
186
+ for ID) optional with default `None` value, but what to do with `birthdate`? When
187
+ parsing we will propagate default `None` so we do not know if this `None` was passed
188
+ explicitly or we've implicitly set it via DTO default.
189
+
190
+ `Maybe` allows to explicitly separate this cases, allowing us to have `None` as present value:
191
+
192
+ ```python
193
+ from dataclasses import dataclass
194
+ from datetime import date
195
+
196
+ from wird import Empty, Maybe
197
+
198
+
199
+ @dataclass
200
+ class CustomerUpdate:
201
+ uid: int
202
+ first_name: Maybe[str] = Empty()
203
+ second_name: Maybe[str] = Empty()
204
+ birthdate: Maybe[date | None] = Empty()
205
+ ```
206
+
207
+ Thus when `birthdate` is `Empty` we know that we do not have to update this column at
208
+ all, and when it is `Some` we can safely set `None` as desired value.
209
+
210
+ `Maybe` provides the following interface:
211
+
212
+ - `Maybe.unwrap` - extract internally stored value on `Some`, raise `EmptyUnwrapError`
213
+ on `Empty`
214
+ - `Maybe.unwrap_or` - extract internally stored value on `Some` or return passed
215
+ replacement value on `Empty`
216
+ - `Maybe.unwrap_or_none` - extract internally stored value on `Some` or return `None`
217
+ on `Empty`
218
+ - `Maybe.unwrap_or_else` - extract internally stored value on `Some` or return result of
219
+ execution of factory function for replacement value on `Empty`
220
+ - `Maybe.unwrap_or_else_async` - same as `Maybe.unwrap_or_else`, but for async factory
221
+ function
222
+ - `Maybe.map` - binding method for sync functions, applies only on `Some`
223
+ - `Maybe.map_async` - same as `Maybe.map`, but for async functions
224
+ - `Maybe.inspect` - binding method for sync side-effect functions, applies only on
225
+ `Some`
226
+ - `Maybe.inspect_async` - same as `Maybe.inspect`, but for async functions
227
+ - `Maybe.and_` - logical AND for 2 `Maybe` values, replaces self `Maybe` with passed
228
+ `Maybe` if first one is `Some`
229
+ - `Maybe.and_then` - same as `Maybe.map`, but for sync functions that return `Maybe`
230
+ - `Maybe.and_then_async` - same as `Maybe.and_then`, but for async functions
231
+ - `Maybe.or_` - logical OR for 2 `Maybe` values, replaces self `Maybe` with passed
232
+ `Maybe` if first one is `Empty`
233
+ - `Maybe.or_else` - replaces `Empty` with `Maybe` result of passed sync function
234
+ - `Maybe.or_else_async` - same as `Maybe.or_else`, but for async functions
235
+ - `Maybe.is_some` - `True` on `Some` container
236
+ - `Maybe.is_some_and` - `True` on `Some` container and passed predicate being `True`
237
+ - `Maybe.is_some_and_async` - same as `Maybe.is_some_and`, but for async predicates
238
+ - `Maybe.is_empty` - `True` on `Empty` container
239
+ - `Maybe.is_empty_or` - `True` on `Empty` container or passed predicate being `True`
240
+ - `Maybe.is_empty_or_async` - same as `Maybe.is_empty_or`, but for async predicates
241
+ - `Maybe.filter` - if predicate is `False` replaces `Maybe` with `Empty`
242
+ - `Maybe.filter_async` - same as `Maybe.filter`, but for async predicates
243
+
244
+ In order to provide seamless experience, instead of making developer to work with
245
+ `Future[Maybe[T]]` we provide `FutureMaybe` container that provides exactly the same
246
+ interface as sync `Maybe`. Worth noting that `FutureMaybe` is awaitable, like `Future`,
247
+ and returns internally stored `Maybe` instance.
248
+
249
+ Also in some cases one might need point-free versions of `Maybe` interface methods, so
250
+ one can access them via `maybe` module. For `FutureMaybe` point-free functions one can
251
+ use `future_maybe` module.
252
+
253
+ ### `Result`
254
+
255
+ Exception handling is one of the most important tasks in development. We often face
256
+ cases when invocation of some logic can lead to Exception raise. In python default
257
+ handling mechanism is `try` - `except` - `finally` block, which is actually just another
258
+ for of `if` statement.
259
+
260
+ Worst thing about this approach is that commonly in python the only way to know that
261
+ function can raise an exception is documentation (which is not always written or written
262
+ good). There is no explicit mechanism to tell LSP / type checker / linter, that this
263
+ specific function needs exception handling.
264
+
265
+ `Result` monad provides another approach, which is common for Rust and Go developers -
266
+ let's return exceptions instead of raising them. Thus we can explicitly tell that soma
267
+ action can fail and requires edge-case handling.
268
+
269
+ Like `Maybe`, `Result` is just a protocol and has 2 implementations:
270
+
271
+ - `Ok` - container indicating that calculation succeeded
272
+ - `Err` - container indicating that calculation failed
273
+
274
+ Simplest case of using `Result` is division:
275
+
276
+ ```python
277
+ from wird import Result, Ok, Err
278
+
279
+ def try_div(a: int, b: int) -> Result[float, ZeroDivisionError]:
280
+ if b == 0:
281
+ return Err(ZeroDivisionError())
282
+
283
+ return Ok(a / b)
284
+ ```
285
+
286
+ There we explicitly tell that division operation can lead to failure and even pinpoint
287
+ specific type of error.
288
+
289
+ `Result` provides the following interface:
290
+
291
+ - `Result.unwrap` - extract internally stored value of `Ok` or raise `ErrUnwrapError`
292
+ - `Result.unwrap_or` - extract internally stored value of `Ok` or return other
293
+ - `Result.unwrap_or_else` - extract internally stored value of `Ok` or return closure
294
+ result
295
+ - `Result.unwrap_or_else_async` - same as `Result.unwrap_or_else`, but for async
296
+ closures
297
+ - `Result.unwrap_err` - same as `Result.unwrap`, but for `Err`
298
+ - `Result.unwrap_err_or` - same as `Result.unwrap_err_or`, but for `Err`
299
+ - `Result.unwrap_err_or_else` - same as `Result.unwrap_err_or_else`, but for `Err`
300
+ - `Result.unwrap_err_or_else_async` - same as `Result.unwrap_err_or_else_async`, but for
301
+ `Err`
302
+ - `Result.map` - binding method for `Ok`
303
+ - `Result.map_async` - same as `Result.map`, but for async functions
304
+ - `Result.inspect` - binding side-effect method for `Ok`
305
+ - `Result.inspect_async`- same as `Result.inspect_async`, but for async functions
306
+ - `Result.map_err` - same as `Result.map`, but for `Err`
307
+ - `Result.map_err_async` - same as `Result.map_async`, but for `Err`
308
+ - `Result.inspect_err` - same as `Result.inspect`, but for `Err`
309
+ - `Result.inspect_err_async` - same as `Result.inspect_async`, but for `Err`
310
+ - `Result.and_` - logical AND, replaces current `Result` with passed on `Ok`
311
+ - `Result.and_then` - same as `Result.map`, but for functions returning `Result`
312
+ - `Result.and_then_async` - same as `Result.and_then`, but for async functions
313
+ - `Result.or_` - logical OR, replaces current `Result` with passed on `Err`
314
+ - `Result.or_else` - same as `Result.map_err`, but for functions returning `Result`
315
+ - `Result.or_else_async` - same as `Result.or_else`, but for async functions
316
+ - `Result.is_ok` - `True` on `Ok`
317
+ - `Result.is_ok_and` - `True` on `Ok` and predicate `True`
318
+ - `Result.is_ok_and_async` - same as `Result.is_ok_and`, but for async predicate
319
+ - `Result.is_ok_or` - `True` on `Ok` or `Err` predicate `True`
320
+ - `Result.is_ok_or_async` - same as `Result.is_ok_or`, but for async predicate
321
+ - `Result.is_err` - `True` on `Err`
322
+ - `Result.is_err_and` - `True` on `Err` and predicate `True`
323
+ - `Result.is_err_and_async` - same as `Result.is_err_and`, but for async predicate
324
+ - `Result.is_err_or` - `True` on `Err` or `Ok` predicate `True
325
+ - `Result.is_err_or_async` - same as `result.is_err_or`, but for async predicate
326
+
327
+ In the same manner as with `Maybe` we wird provides:
328
+
329
+ - `FutureResult` as seamless adapter for `Future[Result]`
330
+ - point-free `Result` API in `wird.result` module
331
+ - point-free `FutureResult` API in `wird.future_result` module