Coverage for greyhorse / river / private / monads / rr.py: 100%
23 statements
« prev ^ index » next coverage.py v7.14.0, created at 2026-05-18 11:33 +0300
« prev ^ index » next coverage.py v7.14.0, created at 2026-05-18 11:33 +0300
1# mypy: warn_no_return=false,disable_error_code="return-value"
2from __future__ import annotations
4from collections.abc import Callable
6from greyhorse.result import Err, Ok, Result
9class RR[R, T, E]:
10 """Reader + Result monad — computation that depends on environment and can fail.
12 Wraps a function ``R -> Result[T, E]``. Short-circuits on ``Err``.
14 Type parameters:
15 R: Environment type.
16 T: Success value type.
17 E: Error type.
19 Examples:
20 ::
22 rr = RR(lambda r: Ok(r + 1))
23 assert rr(10).unwrap() == 11
25 # Err short-circuits the chain
26 err_rr = RR(lambda r: Err('fail'))
27 chained = err_rr.and_then(lambda v: RR(lambda r: Ok(v * 2)))
28 assert chained(5).is_err()
29 """
31 __slots__ = ('_run',)
33 def __init__(self, function: Callable[[R], Result[T, E]]) -> None:
34 self._run = function
36 def map[U](self, f: Callable[[T], U]) -> RR[R, U, E]:
37 """Apply a pure function to the success value."""
38 def run(r: R) -> Result[U, E]:
39 a = self._run(r)
40 return a.map(f)
42 return RR(run)
44 def and_then[U](self, f: Callable[[T], RR[R, U, E]]) -> RR[R, U, E]:
45 """Monadic bind — chain on success, short-circuit on error."""
46 def run(r: R) -> Result[U, E]:
47 a = self._run(r)
48 match a:
49 case Ok(v):
50 return f(v)(r)
51 case Err(_):
52 return a
54 return RR(run)
56 def __call__(self, r: R) -> Result[T, E]:
57 """Execute the computation with the given environment."""
58 return self._run(r)