Skip to content

Commit e81d48f

Browse files
committed
doc: add method chaining example to readme.md
1 parent b695acd commit e81d48f

File tree

1 file changed

+193
-4
lines changed

1 file changed

+193
-4
lines changed

README.md

Lines changed: 193 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ The `ResultContainer` is designed to streamline error propagation and improve co
2121
- **Variants for Success and Failure**: Two variants represented in a Result instance, `Ok(value)` for successful outcomes, and `Err(e)` for errors that have resulted. Provides a flexible mechanism for chaining operations on the `Ok` value while propagating errors through `Err`.
2222
- **Attribute and Method Transparency**: Automatically passes attributes, methods, indices, and math operations to the value contained within an `Ok`, otherwise propagates the `Err(e)`.
2323
- **Utility Methods**: Implements helper methods for error propagation, transformation, and querying (e.g., `.map()`, `.apply()`, `.unwrap_or()`, `.expect()`, `.raises()`) for concise and readable handling of success and error cases.
24+
- **Functional Programming**: Provides an easy way to track errors when method chaining.
2425

2526
## Installation
2627
To install the module
@@ -432,7 +433,7 @@ bad_dt.raises() # raises a ResultErr exception
432433

433434

434435

435-
### Passing Functions and Chaining Operations
436+
### Functions
436437

437438
```python
438439
from ResultContainer import Result, Ok, Err
@@ -446,12 +447,200 @@ c = Ok(-9) # Ok(-9)
446447
d = c.apply(sqrt) # Err("Result.apply exception. | ValueError: math domain error")
447448
e = sqrt(c.expect()) # raises an error
448449

450+
451+
```
452+
453+
454+
455+
### Method Chaining
456+
457+
```python
458+
from ResultContainer import Result, Ok, Err
459+
from math import sqrt
460+
449461
plus1 = lambda x: x + 1
462+
a = Ok(2)
463+
b = (a / 0).map_or(10, plus1).map_or(20, plus1).map_or(30, plus1) # Err(div/0) -> Ok(10) -> Ok(11) -> Ok(12)
464+
465+
```
466+
467+
468+
469+
470+
#### Function Chaining
471+
472+
```python
473+
from ResultContainer import Result, Ok, Err
474+
from math import sqrt
475+
476+
# Some functions include `*args` because `apply_or_else(err_func, ok_func)` requires
477+
# that both the err_func and ok_func have the same argument length.
478+
# The *args serves as a place holder to catch the unused extra argument.
479+
#
480+
# For example: `Ok(10).apply_or_else(plus11, div, 2)`
481+
# first evaluates: `div(10, 2)`
482+
# and if the function fails, tries: `plus11(10, 2)`
483+
# where the `2` must be passed, but is not part of the method
484+
485+
def div(x, y): return x / y
486+
487+
def pow2(x): return x**2
488+
489+
def plus11(x, *args): return x + 11 # *args is ignored
490+
491+
def neg(x, *args): return -x # *args is ignored
492+
493+
a = Ok(5)
494+
495+
# ----------------------------------------------------------------------
496+
# The `map` methods raise an exception if the function fails:
497+
#
498+
fail1 = a.map(pow2).map(plus11).map(sqrt).map(neg).map(sqrt)
499+
# 5 -> 25 -> 36 -> 6 -> -6 -> raise ValueError
500+
501+
fail2 = a.map(pow2).map(plus11).map(sqrt).map(neg).map_or(None, sqrt)
502+
# 5 -> 25 -> 36 -> 6 -> -6 -> raise ValueError
503+
# ----------------------------------------------------------------------
504+
#
505+
# Apply returns an Err() if the function fails
506+
# Note, the methods have been split over multiple lines
507+
#
508+
b = (
509+
a.apply(pow2) # Ok(5) -> Ok(25)
510+
.apply(plus11) # -> Ok(36)
511+
.apply(sqrt) # -> Ok(6)
512+
.apply(neg) # -> Ok(-6)
513+
.apply(sqrt) # -> Err("Result.apply exception | ValueError: math domain error") = b
514+
)
515+
516+
c = (
517+
a.apply(pow2) # Ok(5) -> Ok(25)
518+
.apply(plus11) # -> Ok(36)
519+
.apply(sqrt) # -> Ok(6)
520+
.apply(neg) # -> Ok(-6)
521+
.apply_or(None, sqrt) # -> Ok(None) = c
522+
)
523+
524+
d = (
525+
a.apply(pow2) # Ok(5) -> Ok(25)
526+
.apply(plus11) # -> Ok(36)
527+
.apply(sqrt) # -> Ok(6)
528+
.apply(neg) # -> Ok(3)
529+
.apply_or_else(plus11, sqrt) # -> Ok(5) = d
530+
)
531+
532+
e = (
533+
a.apply(pow2) # Ok(5) -> Ok(25)
534+
.apply(plus11) # -> Ok(36)
535+
.apply(sqrt) # -> Ok(6)
536+
.apply(div, 2) # -> Ok(3)
537+
.apply(div, 0) # -> Err("Result.apply exception | ZeroDivisionError: float division by zero") = e
538+
)
539+
540+
f = (
541+
a.apply(pow2) # Ok(5) -> Ok(25)
542+
.apply(plus11) # -> Ok(36)
543+
.apply(sqrt) # -> Ok(6)
544+
.apply(div, 2) # -> Ok(3)
545+
.apply_or_else(plus11, div, 0) # -> Ok(14) = f
546+
)
547+
548+
g = (
549+
a.apply(pow2) # Ok(5) -> Ok(25)
550+
.apply(plus11) # -> Ok(36)
551+
.apply(sqrt) # -> Ok(6)
552+
.apply_or_else(neg, div, 0) # -> Ok(-6)
553+
.apply_or_else(plus11, div, 2) # -> Ok(-3) = g
554+
)
555+
```
556+
557+
558+
559+
560+
#### Anonymous Function Chaining
561+
562+
```python
563+
# Note, the chain methods have been split over multiple lines
564+
from ResultContainer import Result, Ok, Err
565+
from math import sqrt
566+
567+
# div = lambda x, y: x / y
568+
# pow2 = lambda x: x**2
569+
# plus11 = lambda x: x + 11
570+
# neg = lambda x: -x
571+
450572
a = Ok(5)
451-
b = Ok(0)
452-
c = (a / b).map_or(10, plus1).map_or(20, plus1).map_or(30, plus1) # c = Err() -> Ok(10) -> Ok(11) -> Ok(12)
453-
d = (a / 0).map_or(10, plus1).map_or(20, plus1).map_or(30, plus1) # d = Err() -> Ok(10) -> Ok(11) -> Ok(12)
454573

574+
# ----------------------------------------------------------------------
575+
# The `map` methods raise an exception if the function fails:
576+
#
577+
fail1 = (
578+
a.map(lambda x: x**2) # Ok(5) -> Ok(25)
579+
.map(lambda x: x + 11) # -> Ok(36)
580+
.map(sqrt) # -> Ok(6)
581+
.map(lambda x: -x) # -> Ok(-6)
582+
.map(sqrt) # -> raise ValueError
583+
)
584+
585+
fail2 = (
586+
a.map(lambda x: x**2) # Ok(5) -> Ok(25)
587+
.map(lambda x: x + 11) # -> Ok(36)
588+
.map(sqrt) # -> Ok(6)
589+
.map(lambda x: -x) # -> Ok(-6)
590+
.map_or(None, sqrt) # -> raise ValueError
591+
)
592+
# ----------------------------------------------------------------------
593+
#
594+
# Apply returns an Err() if the function fails
595+
596+
#
597+
b = (
598+
a.apply(lambda x: x**2) # Ok(5) -> Ok(25)
599+
.apply(lambda x: x + 11) # -> Ok(36)
600+
.apply(sqrt) # -> Ok(6)
601+
.apply(lambda x: -x) # -> Ok(-6)
602+
.apply(sqrt) # -> Err("Result.apply exception | ValueError: math domain error") = b
603+
)
604+
605+
c = (
606+
a.apply(lambda x: x**2) # Ok(5) -> Ok(25)
607+
.apply(lambda x: x + 11) # -> Ok(36)
608+
.apply(sqrt) # -> Ok(6)
609+
.apply(lambda x: -x) # -> Ok(-6)
610+
.apply_or(None, sqrt) # -> Ok(None) = c
611+
)
612+
613+
d = (
614+
a.apply(lambda x: x**2) # Ok(5) -> Ok(25)
615+
.apply(lambda x: x + 11) # -> Ok(36)
616+
.apply(sqrt) # -> Ok(6)
617+
.apply(lambda x: -x) # -> Ok(-6)
618+
.apply_or_else(lambda x: x + 11, sqrt) # -> Ok(5) = d
619+
)
620+
621+
e = (
622+
a.apply(lambda x: x**2) # Ok(5) -> Ok(25)
623+
.apply(lambda x: x + 11) # -> Ok(36)
624+
.apply(sqrt) # -> Ok(6)
625+
.apply(lambda x, y: x / y, 2) # -> Ok(3)
626+
.apply(lambda x, y: x / y, 0) # -> Err("Result.apply exception | ZeroDivisionError: float division by zero") = e
627+
)
628+
629+
f = (
630+
a.apply(lambda x: x**2) # Ok(5) -> Ok(25)
631+
.apply(lambda x: x + 11) # -> Ok(36)
632+
.apply(sqrt) # -> Ok(6)
633+
.apply(lambda x, y: x / y, 2) # -> Ok(3)
634+
.apply_or_else(lambda x, y: x + 11, lambda x, y: x / y, 0) # -> Ok(14) = f
635+
)
636+
637+
g = (
638+
a.apply(lambda x: x**2) # Ok(5) -> Ok(25)
639+
.apply(lambda x: x + 11) # -> Ok(36)
640+
.apply(sqrt) # -> Ok(6)
641+
.apply_or_else(lambda x, y: -x, lambda x, y: x / y, 0) # -> Ok(-6)
642+
.apply_or_else(lambda x, y: x + 11, lambda x, y: x / y, 2) # -> Ok(-3)
643+
)
455644
```
456645

457646

0 commit comments

Comments
 (0)