Coverage for greyhorse / river / private / monads / rsr.py: 97%

33 statements  

« 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="arg-type,return-value" 

2from __future__ import annotations 

3 

4from collections.abc import Callable 

5 

6from greyhorse.result import Err, Ok, Result 

7 

8 

9class RSR[R, S, T, E]: 

10 """Reader + State + Result monad — environment, state, and fallible computation. 

11 

12 Wraps ``(R, S) -> (Result[T, E], S)``. Short-circuits on ``Err``, threads state. 

13 

14 Type parameters: 

15 R: Environment type. 

16 S: State type. 

17 T: Success value type. 

18 E: Error type. 

19 """ 

20 

21 __slots__ = ('_run',) 

22 

23 def __init__(self, function: Callable[[R, S], tuple[Result[T, E], S]]) -> None: 

24 self._run = function 

25 

26 def map[U](self, f: Callable[[T], U]) -> RSR[R, S, U, E]: 

27 """Apply a pure function to the success value, preserving state.""" 

28 def run(r: R, s: S) -> tuple[Result[U, E], S]: 

29 a, s = self._run(r, s) 

30 return a.map(f), s 

31 

32 return RSR(run) 

33 

34 def and_then[U](self, f: Callable[[T], RSR[R, S, U, E]]) -> RSR[R, S, U, E]: 

35 """Monadic bind — chain on success, short-circuit on error.""" 

36 def run(r: R, s: S) -> tuple[Result[U, E], S]: 

37 a, s1 = self._run(r, s) 

38 match a: 

39 case Ok(v): 

40 b, s2 = f(v)(r, s1) 

41 return b, s2 

42 case Err(_): 

43 return a, s1 

44 

45 return RSR(run) 

46 

47 def __call__(self, r: R, state: S) -> tuple[Result[T, E], S]: 

48 """Execute the computation with environment and initial state.""" 

49 return self._run(r, state) 

50 

51 @staticmethod 

52 def pure(value: Result[T, E]) -> RSR[R, S, T, E]: 

53 """Wrap a Result value, passing state through unchanged.""" 

54 return RSR(lambda r, s: (value, s)) 

55 

56 @staticmethod 

57 def get() -> RSR[R, S, T, E]: 

58 """Return the current state as value.""" 

59 return RSR(lambda r, s: (s, s)) 

60 

61 @staticmethod 

62 def put(s: S) -> RSR[R, S, None, E]: 

63 """Replace the current state.""" 

64 return RSR(lambda r, _: (Ok(), s))