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

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

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

3

4

5 class 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)