|
142 | 142 | is amazing stuff. |
143 | 143 | - **option** - an optional world for working with optional values. |
144 | 144 | - **result** - an error handling world for working with result values. |
| 145 | + - **seq** - a world for working with sequences. |
| 146 | + - **async_result** - an asynchronous error handling world for working |
| 147 | + with asynchronous result values. |
| 148 | + - **async_option** - an asynchronous optional world for working with |
| 149 | + asynchronous optional values. |
145 | 150 | - **Mailbox Processor**: for lock free programming using the [Actor |
146 | 151 | model](https://en.wikipedia.org/wiki/Actor_model). |
147 | 152 | - **Cancellation Token**: for cancellation of asynchronous (and |
@@ -461,6 +466,145 @@ def fn5() -> Generator[int, int, int]: |
461 | 466 | pinned to `Exception` i.e., `Result[TSource, Exception]`. |
462 | 467 | """ |
463 | 468 |
|
| 469 | +# %% [markdown] |
| 470 | +""" |
| 471 | +### AsyncResult |
| 472 | +
|
| 473 | +The `AsyncResult[T, TError]` type is the asynchronous version of `Result`. It allows you |
| 474 | +to compose asynchronous operations that may fail, using the Result type. This is |
| 475 | +particularly useful for handling errors in asynchronous code, such as API calls, |
| 476 | +database operations, or any other I/O-bound tasks. |
| 477 | +
|
| 478 | +Similar to the `Result` effect, AsyncResult enables "railway oriented programming" but |
| 479 | +for asynchronous operations. If any part of the function yields an `Error`, the function |
| 480 | +is short-circuited and the following statements will never be executed. |
| 481 | +""" |
| 482 | + |
| 483 | +# %% |
| 484 | +from collections.abc import AsyncGenerator |
| 485 | + |
| 486 | +from expression import Error, Ok, effect |
| 487 | + |
| 488 | + |
| 489 | +@effect.async_result[int, str]() |
| 490 | +async def fn() -> AsyncGenerator[int, int]: |
| 491 | + x: int = yield 42 # Regular value |
| 492 | + y: int = yield await Ok(43) # Awaitable Ok value |
| 493 | + |
| 494 | + # Short-circuit if condition is met |
| 495 | + if x + y > 80: |
| 496 | + z: int = yield await Error("Value too large") # This will short-circuit |
| 497 | + else: |
| 498 | + z: int = yield 44 |
| 499 | + |
| 500 | + yield x + y + z # Final value |
| 501 | + |
| 502 | + |
| 503 | +# This would be run in an async context |
| 504 | +# result = await fn() |
| 505 | +# assert result == Error("Value too large") |
| 506 | + |
| 507 | +# %% [markdown] |
| 508 | +""" |
| 509 | +AsyncResult works well with other async functions and can be nested: |
| 510 | +""" |
| 511 | + |
| 512 | + |
| 513 | +# %% |
| 514 | +@effect.async_result[int, str]() |
| 515 | +async def inner(x: int) -> AsyncGenerator[int, int]: |
| 516 | + y: int = yield x + 1 |
| 517 | + yield y + 1 # Final value is y + 1 |
| 518 | + |
| 519 | + |
| 520 | +@effect.async_result[int, str]() |
| 521 | +async def outer() -> AsyncGenerator[int, int]: |
| 522 | + x: int = yield 40 |
| 523 | + |
| 524 | + # Call inner and await its result |
| 525 | + inner_result = await inner(x) |
| 526 | + y: int = yield await inner_result |
| 527 | + |
| 528 | + yield y # Final value is y |
| 529 | + |
| 530 | + |
| 531 | +# This would be run in an async context |
| 532 | +# result = await outer() |
| 533 | +# assert result == Ok(42) # 40 -> 41 -> 42 |
| 534 | + |
| 535 | +# %% [markdown] |
| 536 | +""" |
| 537 | +A simplified type called `AsyncTry` is also available. It's an async result type that is |
| 538 | +pinned to `Exception` i.e., `AsyncResult[TSource, Exception]`. |
| 539 | +""" |
| 540 | + |
| 541 | +# %% [markdown] |
| 542 | +""" |
| 543 | +### AsyncOption |
| 544 | +
|
| 545 | +The `AsyncOption[T]` type is the asynchronous version of `Option`. It allows you to |
| 546 | +compose asynchronous operations that may return an optional value, using the Option type. |
| 547 | +This is particularly useful for handling optional values in asynchronous code, such as |
| 548 | +API calls that might not return a value, database queries that might not find a record, |
| 549 | +or any other I/O-bound tasks that might not produce a meaningful result. |
| 550 | +
|
| 551 | +Similar to the `Option` effect, AsyncOption enables short-circuiting but for asynchronous |
| 552 | +operations. If any part of the function yields `Nothing`, the function is short-circuited |
| 553 | +and the following statements will never be executed. |
| 554 | +""" |
| 555 | + |
| 556 | +# %% |
| 557 | +from collections.abc import AsyncGenerator |
| 558 | + |
| 559 | +from expression import Nothing, Some, effect |
| 560 | + |
| 561 | + |
| 562 | +@effect.async_option[int]() |
| 563 | +async def fn_option() -> AsyncGenerator[int, int]: |
| 564 | + x: int = yield 42 # Regular value |
| 565 | + y: int = yield await Some(43) # Awaitable Some value |
| 566 | + |
| 567 | + # Short-circuit if condition is met |
| 568 | + if x + y > 80: |
| 569 | + z: int = yield await Nothing # This will short-circuit |
| 570 | + else: |
| 571 | + z: int = yield 44 |
| 572 | + |
| 573 | + yield x + y + z # Final value |
| 574 | + |
| 575 | + |
| 576 | +# This would be run in an async context |
| 577 | +# result = await fn_option() |
| 578 | +# assert result is Nothing |
| 579 | + |
| 580 | +# %% [markdown] |
| 581 | +""" |
| 582 | +AsyncOption works well with other async functions and can be nested: |
| 583 | +""" |
| 584 | + |
| 585 | + |
| 586 | +# %% |
| 587 | +@effect.async_option[int]() |
| 588 | +async def inner_option(x: int) -> AsyncGenerator[int, int]: |
| 589 | + y: int = yield x + 1 |
| 590 | + yield y + 1 # Final value is y + 1 |
| 591 | + |
| 592 | + |
| 593 | +@effect.async_option[int]() |
| 594 | +async def outer_option() -> AsyncGenerator[int, int]: |
| 595 | + x: int = yield 40 |
| 596 | + |
| 597 | + # Call inner and await its result |
| 598 | + inner_result = await inner_option(x) |
| 599 | + y: int = yield await inner_result |
| 600 | + |
| 601 | + yield y # Final value is y |
| 602 | + |
| 603 | + |
| 604 | +# This would be run in an async context |
| 605 | +# result = await outer_option() |
| 606 | +# assert result == Some(42) # 40 -> 41 -> 42 |
| 607 | + |
464 | 608 | # %% [markdown] |
465 | 609 | """ |
466 | 610 | ### Sequence |
|
0 commit comments