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
« 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
6class Some(Option[T]):
8 __match_args__ = ("_value",)
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
15 # ========================================
16 # Rust / Scala Common API
17 # ========================================
19 def is_defined(self) -> bool:
20 return True
22 def is_empty(self) -> bool:
23 return False
25 def map(self, func: Callable[[T], U]) -> Option[U]:
26 result = func(self._value)
27 return Option(result)
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]
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))
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()
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()
63 def filter(self, predicate: Callable[[T], bool]) -> Option[T]:
64 if predicate(self._value):
65 return self
66 else:
67 return Nil()
69 # ========================================
70 # RUST API
71 # ========================================
73 def is_some(self) -> bool:
74 """Rust-style: Return True since this is Some."""
75 return True
77 def is_none(self) -> bool:
78 """Rust-style: Return False since this is Some."""
79 return False
81 def unwrap(self) -> T:
82 """Rust-style: Get the value."""
83 return self._value
85 def unwrap_or(self, default: T) -> T:
86 """Rust-style: Get the value, ignoring default."""
87 return self._value
89 def unwrap_or_else(self, func: Callable[[], T]) -> T:
90 """Rust-style: Get the value, not calling func."""
91 return self._value
93 def expect(self, message: str) -> T:
94 """Rust-style: Get the value. For Some, message is ignored."""
95 return self._value
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)
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
105 def and_(self, other: Option[U]) -> Option[U]:
106 """Rust-style: Return other since self is Some."""
107 return other
109 def or_(self, other: Option[T]) -> Option[T]:
110 """Rust-style: Return self since self is Some."""
111 return self
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
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]
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]
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]
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)
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)
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)
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)
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()
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
163 # ========================================
164 # SCALA API
165 # ========================================
167 def get(self) -> T:
168 return self._value
170 def get_or_else(self, default: Union[T, Callable[[], T]]) -> T:
171 return self._value
173 def or_else(self, alternative: Union[Option[T], Callable[[], Option[T]]]) -> Option[T]:
174 return self
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()
182 def flat_map(self, func: Callable[[T], Option[U]]) -> Option[U]:
183 return func(self._value)
185 def fold(self, if_empty: U, func: Callable[[T], U]) -> U:
186 return func(self._value)
188 def foreach(self, func: Callable[[T], Any]) -> None:
189 """Scala-style: Apply func to value."""
190 func(self._value)
192 def exists(self, predicate: Callable[[T], bool]) -> bool:
193 return predicate(self._value)
195 def forall(self, predicate: Callable[[T], bool]) -> bool:
196 """Scala-style: True if value satisfies predicate."""
197 return predicate(self._value)
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]
203 def non_empty(self) -> bool:
204 """Scala-style: True since Some is not empty."""
205 return True
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]
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]
215 def to_list(self) -> list[T]:
216 return [self._value]
218 def to_optional(self) -> Optional[T]:
219 return self._value
222 # ========================================
223 # PYTHON SPECIAL METHODS
224 # ========================================
226 def __bool__(self) -> bool:
227 return True
229 def __iter__(self) -> Iterator[T]:
230 yield self._value
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
238 def __repr__(self) -> str:
239 return f"Some({self._value!r})"
241 def __str__(self) -> str:
242 return self.__repr__()