Coverage for greyhorse / river / private / monads / rws.py: 100%
31 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: disable_error_code="operator"
2from __future__ import annotations
4from collections.abc import Callable
7class RWS[R, X, S, T]:
8 """Reader + Writer + State monad — environment, output, and mutable state.
10 Wraps ``(R, S) -> (T, S, X)``. Writer outputs combined with ``+``.
12 Type parameters:
13 R: Environment type.
14 X: Writer output type (must support ``+``).
15 S: State type.
16 T: Result type.
17 """
19 __slots__ = ('_run',)
21 def __init__(self, run: Callable[[R, S], tuple[T, S, X]]) -> None:
22 self._run = run
24 def map[U](self, f: Callable[[T], U]) -> RWS[R, X, S, U]:
25 """Apply a pure function to the result."""
26 def run(r: R, s: S) -> tuple[U, S, X]:
27 a, s, x = self._run(r, s)
28 return f(a), s, x
30 return RWS(run)
32 def and_then[U](self, f: Callable[[T], RWS[R, X, S, U]]) -> RWS[R, X, S, U]:
33 """Monadic bind — chain, threading state and concatenating outputs."""
34 def run(r: R, s: S) -> tuple[U, S, X]:
35 a, s1, x1 = self._run(r, s)
36 b, s2, x2 = f(a)(r, s1)
37 return b, s2, x1 + x2
39 return RWS(run)
41 def __call__(self, r: R, state: S) -> tuple[T, S, X]:
42 """Execute the computation with environment and initial state."""
43 return self._run(r, state)
45 @staticmethod
46 def tell(x: X) -> RWS[R, X, S, None]:
47 """Append to the writer output."""
48 return RWS(lambda r, s: (None, s, x))
50 @staticmethod
51 def pure(value: T, monoid: X) -> RWS[R, X, S, T]:
52 """Wrap a pure value with initial writer output."""
53 return RWS(lambda r, s: (value, s, monoid))
55 @staticmethod
56 def get(monoid: X) -> RWS[R, X, S, T]:
57 """Return the current state as value."""
58 return RWS(lambda r, s: (s, s, monoid))
60 @staticmethod
61 def put(s: S, monoid: X) -> RWS[R, X, S, None]:
62 """Replace the current state."""
63 return RWS(lambda r, _: (None, s, monoid))