Coverage for src/monadc/either/right.py: 100%

84 statements  

« prev     ^ index     » next       coverage.py v7.10.2, created at 2025-08-19 20:24 -0700

1from typing import Callable, Union, Any, Optional, cast 

2from .either import Either, L, R, U 

3 

4 

5class Right(Either[L, R]): 

6 """ 

7 Represents a successful computation in an Either monad. 

8 

9 Right is right-biased, meaning transformations (map, flat_map) operate on Right values 

10 and pass through Left values unchanged. 

11 """ 

12 

13 __match_args__ = ("_value",) 

14 

15 def __new__(cls, value: R) -> 'Right[L, R]': 

16 """Create a new Right instance directly, bypassing Either.__new__.""" 

17 return object.__new__(cls) 

18 

19 def __init__(self, value: Any = None, **kwargs: Any) -> None: 

20 # If _value already exists, this is a second call from Python's object creation process 

21 # We should ignore it since the object has already been properly initialized 

22 if hasattr(self, '_value'): 

23 return 

24 

25 # Handle factory construction: Either(right=x) calls Right.__init__(right_obj, right=x) 

26 if 'right' in kwargs: 

27 self._value = kwargs['right'] 

28 # Handle direct construction: Right(value) 

29 else: 

30 self._value = value 

31 

32 def __eq__(self, other: object) -> bool: 

33 if isinstance(other, Right): 

34 return self._value == other._value # type: ignore[no-any-return] 

35 return False 

36 

37 def __repr__(self) -> str: 

38 return f"Right({self._value!r})" 

39 

40 def __str__(self) -> str: 

41 return self.__repr__() 

42 

43 # ======================================== 

44 # Rust / Scala Common API 

45 # ======================================== 

46 

47 def is_left(self) -> bool: 

48 return False 

49 

50 def is_right(self) -> bool: 

51 return True 

52 

53 def left(self) -> Any: # Returns Option[L] 

54 from ..option import Nil 

55 return Nil() 

56 

57 def right(self) -> Any: # Returns Option[R] 

58 from ..option import Some 

59 return Some(self._value) 

60 

61 def map(self, func: Callable[[Union[L, R]], U]) -> U: 

62 return func(self._value) 

63 

64 def map_left(self, func: Callable[[L], U]) -> Either[U, R]: 

65 # Right is unchanged by map_left 

66 return cast(Either[U, R], self) 

67 

68 def map_right(self, func: Callable[[R], U]) -> Either[L, U]: 

69 result = func(self._value) 

70 return Right(result) 

71 

72 # ======================================== 

73 # RUST API 

74 # ======================================== 

75 

76 def either(self, left_func: Callable[[L], U], right_func: Callable[[R], U]) -> U: 

77 """Apply left_func to Left value or right_func to Right value. Alias for fold.""" 

78 return self.fold(left_func, right_func) 

79 

80 def map_either(self, left_func: Callable[[L], U], right_func: Callable[[R], U]) -> U: 

81 return right_func(self._value) 

82 

83 def unwrap_left(self) -> L: 

84 raise ValueError("Cannot unwrap left value from Right") 

85 

86 def unwrap_right(self) -> R: 

87 return self._value # type: ignore[no-any-return] 

88 

89 def expect_left(self, message: str) -> L: 

90 raise ValueError(message) 

91 

92 def expect_right(self, message: str) -> R: 

93 return self._value # type: ignore[no-any-return] 

94 

95 def left_or(self, other: Either[U, R]) -> Either[U, R]: 

96 # Return other since self is Right 

97 return other 

98 

99 def right_or(self, other: Either[L, U]) -> Either[L, U]: 

100 # Return self (Right) rather than other 

101 return cast(Either[L, U], self) 

102 

103 def left_or_else(self, func: Callable[[], Either[U, R]]) -> Either[U, R]: 

104 # Call func and return result since self is Right 

105 return func() 

106 

107 def right_or_else(self, func: Callable[[], Either[L, U]]) -> Either[L, U]: 

108 # Return self (Right) rather than calling func 

109 return cast(Either[L, U], self) 

110 

111 def left_and_then(self, func: Callable[[L], Either[U, R]]) -> Either[U, R]: 

112 # Right passes through unchanged 

113 return cast(Either[U, R], self) 

114 

115 def right_and_then(self, func: Callable[[R], Either[L, U]]) -> Either[L, U]: 

116 return func(self._value) 

117 

118 def flip(self) -> Either[R, L]: 

119 from .left import Left 

120 return Left(self._value) 

121 

122 # ======================================== 

123 # SCALA API 

124 # ======================================== 

125 

126 def swap(self) -> Either[R, L]: 

127 from .left import Left 

128 return Left(self._value) 

129 

130 def fold(self, if_left: Callable[[L], U], if_right: Callable[[R], U]) -> U: 

131 return if_right(self._value) 

132 

133 def foreach(self, func: Callable[[R], Any]) -> None: 

134 func(self._value) 

135 

136 def get(self) -> R: 

137 return self._value # type: ignore[no-any-return] 

138 

139 def get_or_else(self, default: Union[R, Callable[[], R]]) -> R: 

140 # Return the Right value, ignore default 

141 return self._value # type: ignore[no-any-return] 

142 

143 def to_option(self) -> Any: # Returns Option[R] 

144 from ..option import Option 

145 return Option(self._value) 

146 

147 def contains(self, value: Any) -> bool: 

148 return self._value == value # type: ignore[no-any-return] 

149 

150 def exists(self, predicate: Callable[[R], bool]) -> bool: 

151 return predicate(self._value) 

152 

153 def or_else(self, other: Union['Either[L, U]', Callable[[], 'Either[L, U]']]) -> 'Either[L, Union[R, U]]': 

154 # Return self since this is a Right 

155 return cast('Either[L, Union[R, U]]', self)