Coverage for src/monadc/option/some.py: 96%

133 statements  

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

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

2from .option import Option, T, U, V, E 

3from .nil import Nil 

4 

5 

6class Some(Option[T]): 

7 

8 __match_args__ = ("_value",) 

9 

10 def __init__(self, value: T) -> None: 

11 if value is None: 

12 raise ValueError("Cannot create Some with None value") 

13 self._value = value 

14 

15 # ======================================== 

16 # Rust / Scala Common API 

17 # ======================================== 

18 

19 def is_defined(self) -> bool: 

20 return True 

21 

22 def is_empty(self) -> bool: 

23 return False 

24 

25 def map(self, func: Callable[[T], U]) -> Option[U]: 

26 result = func(self._value) 

27 return Option(result) 

28 

29 def flatten(self) -> Option[Any]: 

30 """Flatten Option[Option[T]] to Option[T].""" 

31 if isinstance(self._value, Option): 

32 return self._value # type: ignore[return-value] 

33 return self # type: ignore[return-value] 

34 

35 def transpose(self) -> Any: # Returns Result[Option[T], E] 

36 """Rust-style: Transpose Option[Result[T, E]] to Result[Option[T], E].""" 

37 from ..result import Ok 

38 # If the value is a Result, transpose it 

39 if hasattr(self._value, 'is_ok') and callable(getattr(self._value, 'is_ok')): 

40 if self._value.is_ok(): 

41 return Ok(Some(self._value.unwrap())) 

42 else: 

43 return self._value # Return the Err as-is 

44 # If not a Result, wrap in Ok(Some(...)) 

45 return Ok(Some(self._value)) 

46 

47 def zip(self, other: Option[U]) -> Option[tuple[T, U]]: 

48 """Rust-style: Zip with another Option to create Option of tuple.""" 

49 if other.is_some(): 

50 from .some import Some 

51 return Some((self._value, other.get())) 

52 else: 

53 return Nil() 

54 

55 def unzip(self) -> tuple[Option[Any], Option[Any]]: 

56 """Unzip Option[tuple[A, B]] to tuple[Option[A], Option[B]].""" 

57 if isinstance(self._value, tuple) and len(self._value) == 2: 

58 from .some import Some 

59 return Some(self._value[0]), Some(self._value[1]) 

60 else: 

61 return Nil(), Nil() 

62 

63 def filter(self, predicate: Callable[[T], bool]) -> Option[T]: 

64 if predicate(self._value): 

65 return self 

66 else: 

67 return Nil() 

68 

69 # ======================================== 

70 # RUST API 

71 # ======================================== 

72 

73 def is_some(self) -> bool: 

74 """Rust-style: Return True since this is Some.""" 

75 return True 

76 

77 def is_none(self) -> bool: 

78 """Rust-style: Return False since this is Some.""" 

79 return False 

80 

81 def unwrap(self) -> T: 

82 """Rust-style: Get the value.""" 

83 return self._value 

84 

85 def unwrap_or(self, default: T) -> T: 

86 """Rust-style: Get the value, ignoring default.""" 

87 return self._value 

88 

89 def unwrap_or_else(self, func: Callable[[], T]) -> T: 

90 """Rust-style: Get the value, not calling func.""" 

91 return self._value 

92 

93 def expect(self, message: str) -> T: 

94 """Rust-style: Get the value. For Some, message is ignored.""" 

95 return self._value 

96 

97 def and_then(self, func: Callable[[T], Option[U]]) -> Option[U]: 

98 """Rust-style: Transform Some value to Option.""" 

99 return func(self._value) 

100 

101 def or_else_with(self, func: Callable[[], Option[T]]) -> Option[T]: 

102 """Rust-style: Return self since Some doesn't need alternative.""" 

103 return self 

104 

105 def and_(self, other: Option[U]) -> Option[U]: 

106 """Rust-style: Return other since self is Some.""" 

107 return other 

108 

109 def or_(self, other: Option[T]) -> Option[T]: 

110 """Rust-style: Return self since self is Some.""" 

111 return self 

112 

113 def xor(self, other: Option[T]) -> Option[T]: 

114 """Rust-style: Return Nil if other is Some, self if other is Nil.""" 

115 if other.is_some(): 

116 return Nil() 

117 return self 

118 

119 

120 def get_or_insert(self, value: T) -> T: 

121 """Rust-style: Get the value (ignore insert since Some has value).""" 

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

123 

124 def get_or_insert_with(self, func: Callable[[], T]) -> T: 

125 """Rust-style: Get the value (don't call func since Some has value).""" 

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

127 

128 def insert(self, value: T) -> T: 

129 """Rust-style: Return the inserted value (for Some, return current value).""" 

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

131 

132 def map_or(self, default: U, func: Callable[[T], U]) -> U: 

133 """Rust-style: Apply func to Some value.""" 

134 return func(self._value) 

135 

136 def map_or_else(self, default_func: Callable[[], U], func: Callable[[T], U]) -> U: 

137 """Rust-style: Apply func to Some value (ignore default_func).""" 

138 return func(self._value) 

139 

140 def ok_or(self, error: E) -> Any: # Returns Result[T, E] 

141 """Rust-style: Convert Some(v) to Ok(v).""" 

142 from ..result import Ok 

143 return Ok(self._value) 

144 

145 def ok_or_else(self, func: Callable[[], E]) -> Any: # Returns Result[T, E] 

146 """Rust-style: Convert Some(v) to Ok(v) (don't call func).""" 

147 from ..result import Ok 

148 return Ok(self._value) 

149 

150 def zip_with(self, other: Option[U], func: Callable[[T, U], V]) -> Option[V]: 

151 """Rust-style: Zip with another Option and apply function.""" 

152 if other.is_some(): 

153 from .some import Some 

154 return Some(func(self._value, other.get())) 

155 else: 

156 return Nil() 

157 

158 def inspect(self, func: Callable[[T], Any]) -> Option[T]: 

159 """Rust-style: Call function with value and return self unchanged.""" 

160 func(self._value) 

161 return self 

162 

163 # ======================================== 

164 # SCALA API 

165 # ======================================== 

166 

167 def get(self) -> T: 

168 return self._value 

169 

170 def get_or_else(self, default: Union[T, Callable[[], T]]) -> T: 

171 return self._value 

172 

173 def or_else(self, alternative: Union[Option[T], Callable[[], Option[T]]]) -> Option[T]: 

174 return self 

175 

176 def filter_not(self, predicate: Callable[[T], bool]) -> Option[T]: 

177 if not predicate(self._value): 

178 return self 

179 else: 

180 return Nil() 

181 

182 def flat_map(self, func: Callable[[T], Option[U]]) -> Option[U]: 

183 return func(self._value) 

184 

185 def fold(self, if_empty: U, func: Callable[[T], U]) -> U: 

186 return func(self._value) 

187 

188 def foreach(self, func: Callable[[T], Any]) -> None: 

189 """Scala-style: Apply func to value.""" 

190 func(self._value) 

191 

192 def exists(self, predicate: Callable[[T], bool]) -> bool: 

193 return predicate(self._value) 

194 

195 def forall(self, predicate: Callable[[T], bool]) -> bool: 

196 """Scala-style: True if value satisfies predicate.""" 

197 return predicate(self._value) 

198 

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

200 """Scala-style: True if Option contains the specified value.""" 

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

202 

203 def non_empty(self) -> bool: 

204 """Scala-style: True since Some is not empty.""" 

205 return True 

206 

207 def or_null(self) -> Optional[T]: 

208 """Scala-style: Return value since Some is defined.""" 

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

210 

211 def or_none(self) -> Optional[T]: 

212 """Return value since Some is defined. Alias for or_null.""" 

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

214 

215 def to_list(self) -> list[T]: 

216 return [self._value] 

217 

218 def to_optional(self) -> Optional[T]: 

219 return self._value 

220 

221 

222 # ======================================== 

223 # PYTHON SPECIAL METHODS 

224 # ======================================== 

225 

226 def __bool__(self) -> bool: 

227 return True 

228 

229 def __iter__(self) -> Iterator[T]: 

230 yield self._value 

231 

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

233 if isinstance(other, Some): 

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

235 # Some is never equal to Nil or other types 

236 return False 

237 

238 def __repr__(self) -> str: 

239 return f"Some({self._value!r})" 

240 

241 def __str__(self) -> str: 

242 return self.__repr__()