Coverage for src/monadc/option/nil.py: 97%

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

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

3

4

5 class NilType(Option[T]):

6

7 __match_args__ = ()

8

9 _instance: Optional['NilType[Any]'] = None

10

11 def __new__(cls) -> 'NilType[Any]':

12 if cls._instance is None:

13 cls._instance = cast('NilType[Any]', super().__new__(cls))

14 return cls._instance

15

16 # ========================================

17 # Rust / Scala Common API

18 # ========================================

19

20 def is_defined(self) -> bool:

21 return False

22

23 def is_empty(self) -> bool:

24 return True

25

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

27 # Nil maps to Nil regardless of function

28 return Nil()

29

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

31 """Flatten Nil to Nil."""

32 return cast(Option[Any], self)

33

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

35 """Rust-style: Transpose Nil to Ok(Nil)."""

36 from ..result import Ok

37 return Ok(self)

38

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

40 """Rust-style: Return Nil since self is Nil."""

41 return cast(Option[tuple[T, U]], self)

42

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

44 """Unzip Nil to tuple of Nils."""

45 return cast(Option[Any], self), cast(Option[Any], self)

46

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

48 # Nil filters to Nil regardless of predicate

49 return self

50

51 # ========================================

52 # RUST API

53 # ========================================

54

55 def is_some(self) -> bool:

56 """Rust-style: Return False since this is Nil."""

57 return False

58

59 def is_none(self) -> bool:

60 """Rust-style: Return True since this is Nil."""

61 return True

62

63 def unwrap(self) -> T:

64 """Rust-style: Panic since this is Nil."""

65 raise ValueError("Called unwrap() on a Nil value")

66

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

68 """Rust-style: Return default since this is Nil."""

69 return default

70

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

72 """Rust-style: Call func to compute default since this is Nil."""

73 return func()

74

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

76 """Rust-style: Panic with custom message for Nil."""

77 raise ValueError(message)

78

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

80 """Rust-style: Return Nil since this is Nil."""

81 return cast(Option[U], self)

82

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

84 """Rust-style: Call func to get alternative since self is Nil."""

85 return func()

86

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

88 """Rust-style: Return Nil since self is Nil."""

89 return cast(Option[U], self)

90

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

92 """Rust-style: Return other since self is Nil."""

93 return other

94

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

96 """Rust-style: Return other since self is Nil."""

97 return other

98

99

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

101 """Rust-style: Return Nil since self is Nil."""

102 return cast(Option[V], self)

103

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

105 """Rust-style: Do nothing since self is Nil, return self."""

106 return self

107

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

109 """Rust-style: Return the inserted value since Nil is empty."""

110 return value

111

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

113 """Rust-style: Call func and return result since Nil is empty."""

114 return func()

115

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

117 """Rust-style: Return the inserted value."""

118 return value

119

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

121 """Rust-style: Return default since Nil has no value."""

122 return default

123

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

125 """Rust-style: Call default_func since Nil has no value."""

126 return default_func()

127

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

129 """Rust-style: Convert Nil to Err(error)."""

130 from ..result import Err

131 return Err(error)

132

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

134 """Rust-style: Convert Nil to Err(func())."""

135 from ..result import Err

136 return Err(func())

137

138 # ========================================

139 # SCALA API

140 # ========================================

141

142 def get(self) -> T:

143 raise ValueError("Cannot get value from empty Option")

144

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

146 if callable(default):

147 try:

148 return default()

149 except Exception:

150 raise ValueError("Default function failed and Option is empty")

151 return default

152

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

154 if callable(alternative):

155 try:

156 return alternative()

157 except Exception:

158 return self

159 return alternative

160

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

162 # Nil filters to Nil regardless of predicate

163 return self

164

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

166 # Nil flat_maps to Nil regardless of function

167 return Nil()

168

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

170 # Nil always returns the empty value

171 return if_empty

172

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

174 """Scala-style: Nil does nothing."""

175 pass

176

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

178 # Nil never satisfies any predicate

179 return False

180

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

182 """Scala-style: Nil vacuously satisfies all predicates."""

183 return True

184

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

186 """Scala-style: Nil never contains any value."""

187 return False

188

189 def non_empty(self) -> bool:

190 """Scala-style: False since Nil is empty."""

191 return False

192

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

194 """Scala-style: Return None since Nil is empty."""

195 return None

196

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

198 """Return None since Nil is empty. Alias for or_null."""

199 return None

200

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

202 # Nil converts to empty list

203 return []

204

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

206 # Nil converts to None

207 return None

208

209

210 # ========================================

211 # PYTHON SPECIAL METHODS

212 # ========================================

213

214 def __bool__(self) -> bool:

215 return False

216

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

218 # Nil yields nothing

219 return iter([])

220

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

222 # Nil is equal to other Nil instances

223 return isinstance(other, NilType)

224

225 def __repr__(self) -> str:

226 return "Nil()"

227

228 def __str__(self) -> str:

229 return self.__repr__()

230

231

232# Export singleton class

233 Nil = NilType