Coverage for greyhorse / river / private / monads / rwsr.py: 100%
36 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="operator,return-value,arg-type"
2from __future__ import annotations
4from collections.abc import Callable
6from greyhorse.result import Err, Ok, Result
9class RWSR[R, X, S, T, E]:
10 """Reader + Writer + State + Result monad — the full stack.
12 Wraps ``(R, S) -> (Result[T, E], S, X)``. Short-circuits on ``Err``.
14 Type parameters:
15 R: Environment type.
16 X: Writer output type (must support ``+``).
17 S: State type.
18 T: Success value type.
19 E: Error type.
20 """
22 __slots__ = ('_run',)
24 def __init__(self, run: Callable[[R, S], tuple[Result[T, E], S, X]]) -> None:
25 self._run = run
27 def map[U](self, f: Callable[[T], U]) -> RWSR[R, X, S, U, E]:
28 """Apply a pure function to the success value."""
29 def run(r: R, s: S) -> tuple[Result[U, E], S, X]:
30 a, s, x = self._run(r, s)
31 return a.map(f), s, x
33 return RWSR(run)
35 def and_then[U](self, f: Callable[[T], RWSR[R, X, S, U, E]]) -> RWSR[R, X, S, U, E]:
36 """Monadic bind — chain on success, concatenate outputs."""
37 def run(r: R, s: S) -> tuple[Result[U, E], S, X]:
38 a, s1, x1 = self._run(r, s)
39 match a:
40 case Ok(v):
41 b, s2, x2 = f(v)(r, s1)
42 return b, s2, x1 + x2
43 case Err(_):
44 return a, s1, x1
46 return RWSR(run)
48 @staticmethod
49 def tell(x: X) -> RWSR[R, X, S, None, E]:
50 """Append to the writer output."""
51 return RWSR(lambda r, s: (Ok(), s, x))
53 def __call__(self, r: R, state: S) -> tuple[Result[T, E], S, X]:
54 """Execute the computation with environment and initial state."""
55 return self._run(r, state)
57 @staticmethod
58 def pure(value: Result[T, E], monoid: X) -> RWSR[R, X, S, T, E]:
59 """Wrap a Result value with initial writer output."""
60 return RWSR(lambda r, s: (value, s, monoid))
62 @staticmethod
63 def get(monoid: X) -> RWSR[R, X, S, T, E]:
64 """Return the current state as value."""
65 return RWSR(lambda r, s: (s, s, monoid))
67 @staticmethod
68 def put(s: S, monoid: X) -> RWSR[R, X, S, None, E]:
69 """Replace the current state."""
70 return RWSR(lambda r, _: (Ok(), s, monoid))