sain.option

Rust's Option<T> type. A value that can either be T or None

  1# BSD 3-Clause License
  2#
  3# Copyright (c) 2022-Present, nxtlo
  4# All rights reserved.
  5#
  6# Redistribution and use in source and binary forms, with or without
  7# modification, are permitted provided that the following conditions are met:
  8#
  9# * Redistributions of source code must retain the above copyright notice, this
 10#   list of conditions and the following disclaimer.
 11#
 12# * Redistributions in binary form must reproduce the above copyright notice,
 13#   this list of conditions and the following disclaimer in the documentation
 14#   and/or other materials provided with the distribution.
 15#
 16# * Neither the name of the copyright holder nor the names of its
 17#   contributors may be used to endorse or promote products derived from
 18#   this software without specific prior written permission.
 19#
 20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 21# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 23# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 24# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 25# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 26# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 27# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 28# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 30"""Rust's `Option<T>` type. A value that can either be `T` or `None`"""
 31
 32from __future__ import annotations
 33
 34__all__ = ("Some", "Option", "NOTHING")
 35
 36import typing
 37
 38from . import default as _default
 39from . import iter as _iter
 40from . import macros
 41from . import result as _result
 42from .macros import rustc_diagnostic_item
 43
 44T = typing.TypeVar("T")
 45T_co = typing.TypeVar("T_co", covariant=True)
 46
 47if typing.TYPE_CHECKING:
 48    import collections.abc as collections
 49
 50    U = typing.TypeVar("U")
 51    Fn = collections.Callable[[T], U]
 52    FnOnce = collections.Callable[[], U]
 53
 54
 55@rustc_diagnostic_item("Option")
 56@typing.final
 57class Some(typing.Generic[T], _default.Default["Option[T]"]):
 58    """The `Option` type represents optional value, higher-level abstraction over the `None` type.
 59
 60    It combines union of `T | None` in one convenient structure, allowing the users to manipulate and propagate
 61    the contained value idiomatically.
 62
 63    An `Option` value have multiple use cases:
 64
 65    * Initial values.
 66    * Return value for functions that may or may not contain a return value.
 67    * Optional parameters, class fields.
 68    * Swapping values.
 69
 70    Example
 71    -------
 72    ```py
 73    # the actual implementation of the object.
 74    from sain import Some
 75    # Here `Option` is used for type-hints only, you can include it under `TYPE_CHECKING` if you'd like.
 76    from sain import Option
 77
 78    def divide(numerator: float, denominator: float) -> Option[float]:
 79        if denominator == 0.0:
 80            return Some(None)
 81        return Some(numerator / denominator)
 82
 83    # Returns Option[float]
 84    result = divide(2.0, 3.0)
 85
 86    # Pattern match to retrieve the value
 87    match result:
 88        # The division is valid.
 89        case Some(x):
 90            print("Result:", x)
 91        # Invalid division, this is Some(None)
 92        case _:
 93            print("cannot divide by 0")
 94    ```
 95
 96    ### Converting `None`s into `RuntimeError`s
 97
 98    Sometimes we want to extract the value and cause an error to the caller if it doesn't exist,
 99
100    because handling `Some/None` can be tedious, luckily we have several ways to deal with this.
101
102    ```py
103    def ipaddr(s: str) -> Option[tuple[int, int, int, int]]:
104        match s.split('.'):
105            case [a, b, c, d]:
106                return Some((int(a), int(b), int(c), int(d)))
107            case _:
108                return Some(None)
109
110    # calls `unwrap()` for you.
111    ip = ~ipaddr("192.168.1.19")
112    # causes a `RuntimeError` if it returns `None`.
113    ip = ipaddr("192.168.1.19").unwrap()
114    # causes a `RuntimeError` if it returns `None`, with a context message.
115    ip = ipaddr("192.168.1.19").expect("i need an ip address :<")
116    ```
117
118    The `~` operator will result in `tuple[int, int, int, int]` if the parsing succeed.
119    unless the contained value is `None`, it will cause a `RuntimeError`.
120
121    If the value must be present, use `unwrap_or`, which takes a default parameter
122    and returns it in-case `ipaddr` returns `None`
123    ```py
124    ip = ipaddr("blah blah blah").unwrap_or("192.168.1.255")
125    # Results: 192.168.1.255
126    ```
127
128    Overall, this type provides many other functional methods such as `map`, `filter`.
129
130    boolean checks such as `is_some`, `is_none`, converting between `Option` and `Result` using `ok_or`, and many more.
131    """
132
133    __slots__ = ("_value",)
134    __match_args__ = ("_value",)
135
136    def __init__(self, value: T | None, /) -> None:
137        self._value = value
138
139    @staticmethod
140    def default() -> Option[T]:
141        """Default value for `Option<T>`. Returns `None` wrapped in `Some`.
142
143        Example
144        -------
145        ```py
146        assert Some[int].default() is NOTHING
147        ```
148        """
149        return NOTHING
150
151    # *- Reading the value -*
152
153    def transpose(self) -> T | None:
154        """Convert `Option[T]` into `T | None`.
155
156        Examples
157        --------
158        ```py
159        opt = Some('char')
160        x = opt.transpose()
161        assert x == 'char'
162
163        opt = Some(None)
164        assert opt.transpose() is None
165        ```
166        """
167        return self._value
168
169    def unwrap(self) -> T:
170        """Unwrap the inner value either returning if its not `None` or raising a `RuntimeError`.
171
172        It's usually not recommended to use this method in production code, and instead use safer options such as `unwrap_or` or match patterns.
173
174        Example
175        -------
176        ```py
177        value = Some(5)
178        print(value.unwrap())
179        # 5
180
181        value = Some(None)
182        print(value.unwrap())
183        # RuntimeError
184        ```
185
186        Raises
187        ------
188        `RuntimeError`
189            If the inner value is `None`.
190        """
191        if self._value is None:
192            raise RuntimeError("Called `Option.unwrap()` on `None`.") from None
193
194        return self._value
195
196    def unwrap_or(self, default: T, /) -> T:
197        """Unwrap the inner value either returning if its not `None` or returning `default`.
198
199        Example
200        -------
201        ```py
202        value = Some(5)
203        print(value.unwrap_or(10))
204        # 5
205
206        # Type hint is required here.
207        value: Option[int] = Some(None)
208        print(value.unwrap_or(10))
209        # 10
210        ```
211        """
212        if self._value is None:
213            return default
214
215        return self._value
216
217    def unwrap_or_else(self, f: FnOnce[T], /) -> T:
218        """Unwrap the inner value either returning if its not `None` or calling `f` to get a default value.
219
220        Example
221        -------
222        ```py
223        value = Some(5)
224        print(value.unwrap_or_else(lambda: 10))
225        # 5
226
227        value: Option[bool] = Some(None)
228        print(value.unwrap_or_else(lambda: True))
229        # True
230        ```
231        """
232        if self._value is None:
233            return f()
234
235        return self._value
236
237    @macros.unsafe
238    def unwrap_unchecked(self) -> T:
239        """Returns the contained Some value without checking that the value is not None.
240
241        Example
242        -------
243        ```py
244        v: Option[float] = Some(1.2)
245        v.unwrap_unchecked() # 1.2
246
247        v: Option[float] = Some(None)
248        print(v.unwrap_unchecked()) # Undefined Behavior
249        ```
250        """
251        #! SAFETY: The caller guarantees that the value is not None.
252        return self._value  # pyright: ignore
253
254    def expect(self, message: str, /) -> T:
255        """Returns the contained `Some` value.
256
257        raises if the value is `None` with a custom provided `message`.
258
259        Example
260        -------
261        ```py
262        value = Some("Hello")
263
264        print(value.expect("Value is None"))
265        # "Hello"
266
267        value: Option[str] = Some(None)
268        print(value.expect("Value is None"))
269        # RuntimeError("Value is None")
270        ```
271        """
272        if self._value is None:
273            raise RuntimeError(message)
274
275        return self._value
276
277    # *- object transformation -*
278
279    def map(self, f: Fn[T, U], /) -> Option[U]:
280        """Map the inner value to another type. Returning `Some(None)` if `T` is `None`.
281
282        Example
283        -------
284        ```py
285        value = Some(5.0)
286
287        print(value.map(lambda x: x * 2.0))
288        # Some(10.0)
289
290        value: Option[bool] = Some(None)
291        print(value)
292        # Some(None)
293        ```
294        """
295        if self._value is None:
296            return NOTHING
297
298        return Some(f(self._value))
299
300    def map_or(self, default: U, f: Fn[T, U], /) -> U:
301        """Map the inner value to another type or return `default` if its `None`.
302
303        Example
304        -------
305        ```py
306        value: Option[float] = Some(5.0)
307
308        # map to int.
309        print(value.map_or(0, int))
310        # 6
311
312        value: Option[float] = Some(None)
313        print(value.map_or(0, int)
314        # 0
315        ```
316        """
317        if self._value is None:
318            return default
319
320        return f(self._value)
321
322    def map_or_else(self, default: FnOnce[U], f: Fn[T, U], /) -> U:
323        """Map the inner value to another type, or return `default()` if its `None`.
324
325        Example
326        -------
327        ```py
328        def default() -> int:
329            return sys.getsizeof(object())
330
331        value: Option[float] = Some(5.0)
332
333        # map to int.
334        print(value.map_or_else(default, int))
335        # 6
336
337        value: Option[float] = Some(None)
338        print(value.map_or_else(default, int)
339        # 28 <- size of object()
340        ```
341        """
342        if self._value is None:
343            return default()
344
345        return f(self._value)
346
347    def filter(self, predicate: Fn[T, bool]) -> Option[T]:
348        """Returns `Some(None)` if the contained value is `None`,
349
350        otherwise calls the predicate and returns `Some(T)` if the predicate returns `True`.
351
352        Example
353        -------
354        ```py
355        value = Some([1, 2, 3])
356
357        print(value.filter(lambda x: 1 in x))
358        # Some([1, 2, 3])
359
360        value: Option[int] = Some([1, 2, 3]) # or Some(None)
361        print(value.filter(lambda x: 1 not in x))
362        # None
363        ```
364        """
365        if (value := self._value) is not None:
366            if predicate(value):
367                return Some(value)
368
369        return NOTHING
370
371    def ok_or(self, err: U) -> _result.Result[T, U]:
372        """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err)`.
373
374        Example
375        -------
376        ```py
377        xyz: Option[str] = Some("foo")
378        assert xyz.ok_or(None) == Ok("foo")
379
380        xyz: Option[str] = Some(None)
381        assert xyz.ok_or(None) == Err(None)
382        ```
383        """
384        if self._value is None:
385            return _result.Err(err)
386
387        return _result.Ok(self._value)
388
389    def ok_or_else(self, err: FnOnce[U]) -> _result.Result[T, U]:
390        """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err())`.
391
392        Example
393        -------
394        ```py
395        xyz: Option[str] = Some("foo")
396        assert xyz.ok_or(None) == Ok("foo")
397
398        xyz: Option[str] = Some(None)
399        assert xyz.ok_or(None) == Err(None)
400        ```
401        """
402        if self._value is None:
403            return _result.Err(err())
404
405        return _result.Ok(self._value)
406
407    def zip(self, other: Option[U]) -> Option[tuple[T, U]]:
408        """Zips `self` with `other`.
409
410        if `self` is `Some(s)` and other is `Some(o)`, this returns `Some((s, o))` otherwise `None`.
411
412        Example
413        -------
414        ```py
415        x = Some(1)
416        y = Some("hi")
417        z: Option[str] = Some(None)
418
419        assert x.zip(y) == Some((1, "hi"))
420        assert x.zip(z) == Some(None)
421        ```
422        """
423        if self._value is not None and other._value is not None:
424            return Some((self._value, other._value))
425
426        return NOTHING
427
428    def zip_with(
429        self, other: Option[U], f: collections.Callable[[T, U], T_co]
430    ) -> Option[T_co]:
431        """Zips `self` with `other` using function `f`.
432
433        if `self` is `Some(s)` and other is `Some(o)`, this returns `Some(f(s, o))` otherwise `None`.
434
435        Example
436        -------
437        ```py
438        @dataclass
439        class Point:
440            x: float
441            y: float
442
443        x, y = Some(32.1), Some(42.4)
444        assert x.zip_with(y, Point) == Some(Point(32.1, 42.4))
445        ```
446        """
447        if self._value is not None and other._value is not None:
448            return Some(f(self._value, other._value))
449
450        return NOTHING
451
452    # *- Inner operations *-
453
454    def take(self) -> Option[T]:
455        """Take the value from `self` Setting it to `None`, and then return `Some(v)`.
456
457        If you don't care about the original value, use `Option.clear()` instead.
458
459        Example
460        -------
461        ```py
462        original = Some("Hi")
463        new = original.take()
464
465        print(original, new)
466        # None, Some("Hi")
467        ```
468        """
469        if self._value is None:
470            return NOTHING
471
472        val = self._value
473        self._value = None
474        return Some(val)
475
476    def take_if(self, predicate: collections.Callable[[T], bool]) -> Option[T]:
477        """Take the value from `Self`, Setting it to `None` only if predicate returns `True`.
478
479        If you don't care about the original value, use `Option.clear_if()` instead.
480
481        Example
482        -------
483        ```py
484        def validate(email: str) -> bool:
485            # you can obviously validate this better.
486            return email.find('@') == 1
487
488        original = Some("flex@gg.com")
489        valid = original.take_if(validate)
490        assert is_allowed.is_some() and original.is_none()
491
492        original = Some("mail.example.com")
493        invalid = original.take_if(validate)
494        assert invalid.is_none() and original.is_some()
495        ```
496        """
497        if self.map_or(False, predicate):
498            return self.take()
499
500        return NOTHING
501
502    def clear(self) -> None:
503        """Clear the inner value, setting it to `None`.
504
505        If you care about the original value, use `Option.take()` instead.
506
507        Example
508        -------
509        ```py
510        value = Some("Hello")
511        value.clear()
512        assert value.is_none()
513        ```
514        """
515        self._value = None
516
517    def clear_if(self, predicate: Fn[T, bool]) -> None:
518        """Clear the inner value, setting it to `None` if the predicate returns `True`.
519
520        If you care about the original value, use `Option.take_if()` instead.
521
522        Example
523        -------
524        ```py
525        value = Some("Hello")
526        value.clear_if(lambda x: x == "Hello")
527        assert value.is_none()
528        ```
529        """
530        if self._value is not None and predicate(self._value):
531            self._value = None
532
533    def replace(self, value: T) -> Option[T]:
534        """Replace the contained value with another value.
535
536        Use `Option.insert` if you want to return the original value
537        that got inserted instead of `self`
538
539        Example
540        -------
541        ```py
542        value: Option[str] = Some(None)
543        value.replace("Hello")
544        # Some("Hello")
545        ```
546        """
547        self._value = value
548        return self
549
550    def insert(self, value: T) -> T:
551        """Insert a value into the option, and then return a reference to it.
552
553        This will overwrite the old value if it was already contained.
554
555        Example
556        -------
557        ```py
558        flag: Option[bool] = Some(None)
559        flag_ref = flag.insert(True)
560        assert flag_ref == True
561        assert flag.unwrap() == True
562        ```
563        """
564        self._value = value
565        return value
566
567    def get_or_insert(self, value: T) -> T:
568        """Insert a value into the option if it was `None`,
569        and then return a reference to it.
570
571        Example
572        -------
573        ```py
574        state: Option[bool] = Some(None)
575        assert state.get_or_insert(True) is True
576        assert state.get_or_insert(False) is True
577        ```
578        """
579        if self._value is not None:
580            return self._value
581
582        self._value = value
583        return value
584
585    def get_or_insert_with(self, f: FnOnce[T]) -> T:
586        """Insert a value into the option computed from `f()` if it was `None`,
587        and then return a reference to it.
588
589        Example
590        -------
591        ```py
592        flag: Option[bool] = Some(None)
593        flag_ref = flag.insert(True)
594        assert flag_ref == True
595        assert flag.unwrap() == True
596        ```
597        """
598        if self._value is not None:
599            return self._value
600
601        v = self._value = f()
602        return v
603
604    def and_ok(self, optb: Option[T]) -> Option[T]:
605        """Returns `None` if `self` or `optb` is `None`, otherwise return `optb`.
606
607        aliases: `Option::and`
608
609        Example
610        -------
611        ```py
612        x = Some(1)
613        y: Option[str] = Some(None)
614        assert x.and_ok(y) == Some(None)
615
616        x: Option[str] = Some(None)
617        y = Some(1)
618        assert x.and_ok(y) == Some(None)
619
620        x: Option[str] = Some("hi")
621        y = Some(100)
622        assert x.and_ok(y) == Some(100)
623        ```
624        """
625        if self._value is None or optb._value is None:
626            return optb
627
628        return NOTHING
629
630    def and_then(self, f: Fn[T, Option[T]]) -> Option[T]:
631        """Returns `Some(None)` if the contained value is `None`, otherwise call `f()`
632        on `T` and return `Option[T]`.
633
634        Example
635        -------
636        ```py
637        value = Some(5)
638        print(value.and_then(lambda x: Some(x * 2)))
639        # Some(10)
640
641        value: Option[int] = Some(None)
642        print(value.and_then(lambda x: Some(x * 2)))
643        # Some(None)
644        ```
645        """
646        if self._value is None:
647            return NOTHING
648
649        return f(self._value)
650
651    def inspect(self, f: Fn[T, typing.Any]) -> Option[T]:
652        """Calls `f()` on the contained value if it was `Some(v)`, otherwise does nothing.
653
654        Example
655        -------
656        ```py
657        def debug(x: str) -> None:
658            print("Debugging:", x)
659
660        value = Some("foo")
661        inner = value.inspect(debug).expect("no value to debug")
662        # prints: Debugging: "foo"
663
664        value: Option[str] = Some(None)
665        value.inspect(debug) # prints nothing
666        """
667        if self._value is not None:
668            f(self._value)
669
670        return self
671
672    # *- Builder methods *-
673
674    def iter(self) -> _iter.ExactSizeIterator[T]:
675        """Returns an iterator over the contained value.
676
677        Example
678        -------
679        ```py
680        from sain import Some
681        value = Some("gg").iter()
682        assert value.next() == Some("gg")
683
684        value: Option[int] = Some(None)
685        assert value.iter().next().is_none()
686        ```
687        """
688        if self._value is None:
689            return _iter.empty()
690
691        return _iter.once(self._value)
692
693    # *- Boolean checks *-
694
695    def is_some(self) -> bool:
696        """Returns `True` if the contained value is not `None`, otherwise returns `False`.
697
698        Example
699        -------
700        ```py
701        value = Some(5)
702        print(value.is_some())
703        # True
704
705        value: Option[int] = Some(None)
706        print(value.is_some())
707        # False
708        ```
709        """
710        return self._value is not None
711
712    def is_some_and(self, predicate: Fn[T, bool]) -> bool:
713        """Returns `True` if the contained value is not `None` and
714        the predicate returns `True`, otherwise returns `False`.
715
716        Example
717        -------
718        ```py
719        value = Some(5)
720        print(value.is_some_and(lambda x: x > 3))
721        # True
722
723        value: Option[int] = Some(None)
724        print(value.is_some_and(lambda x: x > 3))
725        # False
726        ```
727        """
728        return self._value is not None and predicate(self._value)
729
730    def is_none(self) -> bool:
731        """Returns `True` if the contained value is `None`, otherwise returns `False`.
732
733        Example
734        -------
735        ```py
736        value = Some(5)
737        print(value.is_none())
738        # False
739
740        value: Option[int] = Some(None)
741        print(value.is_none())
742        # True
743        ```
744        """
745        return self._value is None
746
747    def is_none_or(self, f: Fn[T, bool]) -> bool:
748        """Returns `True` if the contained value is `None` or the predicate returns `True`,
749        otherwise returns `False`.
750
751        Example
752        -------
753        ```py
754        value = Some(5)
755        print(value.is_none_or(lambda x: x > 3))
756        # False
757
758        value: Option[int] = Some(None)
759        print(value.is_none_or(lambda x: x > 3))
760        # True
761        ```
762        """
763        match self._value:
764            case None:
765                return True
766            case x:
767                return f(x)
768
769    def __repr__(self) -> str:
770        if self._value is None:
771            return "None"
772        return f"Some({self._value!r})"
773
774    __str__ = __repr__
775
776    def __invert__(self) -> T:
777        return self.unwrap()
778
779    def __or__(self, other: T) -> T:
780        return self._value if self._value is not None else other
781
782    def __bool__(self) -> bool:
783        return self._value is not None
784
785    def __eq__(self, other: None | object) -> bool:
786        if other is None:
787            return self._value is None
788
789        if not isinstance(other, Some):
790            return NotImplemented
791
792        return self._value == other._value  # pyright: ignore[reportUnknownVariableType, reportUnknownMemberType]
793
794    def __ne__(self, other: object) -> bool:
795        return not self.__eq__(other)
796
797    def __hash__(self) -> int:
798        return hash(self._value)
799
800
801Option: typing.TypeAlias = "Some[T]"
802"""A type hint for a value that can be `Some<T>` or `None`.
803
804The reason this exist is to satisfy the UX with Rust's type system.
805
806Example
807-------
808```py
809from __future__ import annotations
810
811import typing
812from sain import Some
813
814if typing.CHECKING:
815    from sain import Option
816
817foo: Option[str] = Some(None)
818```
819"""
820
821# FIXME: I just realized how unsafe this is since it can be mutated and potentially cause catastrophic bugs.
822# maybe better to just use nothing_unchecked() instead or `Some(None)`.
823NOTHING: typing.Final[Some[typing.Any]] = Some(None)
824"""A constant that is always `Some(None)`.
825
826Example
827-------
828```py
829from sain import NOTHING, Some
830
831place_holder: Option[str] = NOTHING
832assert NOTHING == Some(None) # True
833```
834"""
835
836
837@typing.no_type_check
838@macros.unsafe
839def nothing_unchecked() -> Option[T]:
840    """A placeholder that always returns `sain.NOTHING` but acts like it returns `Option[T]`.
841
842    This is useful to avoid constructing new `Some(None)` and want to return `T` in the future.
843
844    Example
845    -------
846    ```py
847    class User:
848        username: str
849
850        def name(self) -> Option[str]:
851            if '@' not in self.username:
852                # even though the type of `NOTHING` is `Option[None]`.
853                # we trick the type checker into thinking
854                # that its an `Option[str]`.
855                return NOTHING
856
857            return Some(self.username.split('@')[0])
858    ```
859    """
860    return typing.cast("Option[T]", NOTHING)
@rustc_diagnostic_item('Option')
@typing.final
class Some(typing.Generic[~T], sain.default.Default[ForwardRef('Option[T]')]):
 56@rustc_diagnostic_item("Option")
 57@typing.final
 58class Some(typing.Generic[T], _default.Default["Option[T]"]):
 59    """The `Option` type represents optional value, higher-level abstraction over the `None` type.
 60
 61    It combines union of `T | None` in one convenient structure, allowing the users to manipulate and propagate
 62    the contained value idiomatically.
 63
 64    An `Option` value have multiple use cases:
 65
 66    * Initial values.
 67    * Return value for functions that may or may not contain a return value.
 68    * Optional parameters, class fields.
 69    * Swapping values.
 70
 71    Example
 72    -------
 73    ```py
 74    # the actual implementation of the object.
 75    from sain import Some
 76    # Here `Option` is used for type-hints only, you can include it under `TYPE_CHECKING` if you'd like.
 77    from sain import Option
 78
 79    def divide(numerator: float, denominator: float) -> Option[float]:
 80        if denominator == 0.0:
 81            return Some(None)
 82        return Some(numerator / denominator)
 83
 84    # Returns Option[float]
 85    result = divide(2.0, 3.0)
 86
 87    # Pattern match to retrieve the value
 88    match result:
 89        # The division is valid.
 90        case Some(x):
 91            print("Result:", x)
 92        # Invalid division, this is Some(None)
 93        case _:
 94            print("cannot divide by 0")
 95    ```
 96
 97    ### Converting `None`s into `RuntimeError`s
 98
 99    Sometimes we want to extract the value and cause an error to the caller if it doesn't exist,
100
101    because handling `Some/None` can be tedious, luckily we have several ways to deal with this.
102
103    ```py
104    def ipaddr(s: str) -> Option[tuple[int, int, int, int]]:
105        match s.split('.'):
106            case [a, b, c, d]:
107                return Some((int(a), int(b), int(c), int(d)))
108            case _:
109                return Some(None)
110
111    # calls `unwrap()` for you.
112    ip = ~ipaddr("192.168.1.19")
113    # causes a `RuntimeError` if it returns `None`.
114    ip = ipaddr("192.168.1.19").unwrap()
115    # causes a `RuntimeError` if it returns `None`, with a context message.
116    ip = ipaddr("192.168.1.19").expect("i need an ip address :<")
117    ```
118
119    The `~` operator will result in `tuple[int, int, int, int]` if the parsing succeed.
120    unless the contained value is `None`, it will cause a `RuntimeError`.
121
122    If the value must be present, use `unwrap_or`, which takes a default parameter
123    and returns it in-case `ipaddr` returns `None`
124    ```py
125    ip = ipaddr("blah blah blah").unwrap_or("192.168.1.255")
126    # Results: 192.168.1.255
127    ```
128
129    Overall, this type provides many other functional methods such as `map`, `filter`.
130
131    boolean checks such as `is_some`, `is_none`, converting between `Option` and `Result` using `ok_or`, and many more.
132    """
133
134    __slots__ = ("_value",)
135    __match_args__ = ("_value",)
136
137    def __init__(self, value: T | None, /) -> None:
138        self._value = value
139
140    @staticmethod
141    def default() -> Option[T]:
142        """Default value for `Option<T>`. Returns `None` wrapped in `Some`.
143
144        Example
145        -------
146        ```py
147        assert Some[int].default() is NOTHING
148        ```
149        """
150        return NOTHING
151
152    # *- Reading the value -*
153
154    def transpose(self) -> T | None:
155        """Convert `Option[T]` into `T | None`.
156
157        Examples
158        --------
159        ```py
160        opt = Some('char')
161        x = opt.transpose()
162        assert x == 'char'
163
164        opt = Some(None)
165        assert opt.transpose() is None
166        ```
167        """
168        return self._value
169
170    def unwrap(self) -> T:
171        """Unwrap the inner value either returning if its not `None` or raising a `RuntimeError`.
172
173        It's usually not recommended to use this method in production code, and instead use safer options such as `unwrap_or` or match patterns.
174
175        Example
176        -------
177        ```py
178        value = Some(5)
179        print(value.unwrap())
180        # 5
181
182        value = Some(None)
183        print(value.unwrap())
184        # RuntimeError
185        ```
186
187        Raises
188        ------
189        `RuntimeError`
190            If the inner value is `None`.
191        """
192        if self._value is None:
193            raise RuntimeError("Called `Option.unwrap()` on `None`.") from None
194
195        return self._value
196
197    def unwrap_or(self, default: T, /) -> T:
198        """Unwrap the inner value either returning if its not `None` or returning `default`.
199
200        Example
201        -------
202        ```py
203        value = Some(5)
204        print(value.unwrap_or(10))
205        # 5
206
207        # Type hint is required here.
208        value: Option[int] = Some(None)
209        print(value.unwrap_or(10))
210        # 10
211        ```
212        """
213        if self._value is None:
214            return default
215
216        return self._value
217
218    def unwrap_or_else(self, f: FnOnce[T], /) -> T:
219        """Unwrap the inner value either returning if its not `None` or calling `f` to get a default value.
220
221        Example
222        -------
223        ```py
224        value = Some(5)
225        print(value.unwrap_or_else(lambda: 10))
226        # 5
227
228        value: Option[bool] = Some(None)
229        print(value.unwrap_or_else(lambda: True))
230        # True
231        ```
232        """
233        if self._value is None:
234            return f()
235
236        return self._value
237
238    @macros.unsafe
239    def unwrap_unchecked(self) -> T:
240        """Returns the contained Some value without checking that the value is not None.
241
242        Example
243        -------
244        ```py
245        v: Option[float] = Some(1.2)
246        v.unwrap_unchecked() # 1.2
247
248        v: Option[float] = Some(None)
249        print(v.unwrap_unchecked()) # Undefined Behavior
250        ```
251        """
252        #! SAFETY: The caller guarantees that the value is not None.
253        return self._value  # pyright: ignore
254
255    def expect(self, message: str, /) -> T:
256        """Returns the contained `Some` value.
257
258        raises if the value is `None` with a custom provided `message`.
259
260        Example
261        -------
262        ```py
263        value = Some("Hello")
264
265        print(value.expect("Value is None"))
266        # "Hello"
267
268        value: Option[str] = Some(None)
269        print(value.expect("Value is None"))
270        # RuntimeError("Value is None")
271        ```
272        """
273        if self._value is None:
274            raise RuntimeError(message)
275
276        return self._value
277
278    # *- object transformation -*
279
280    def map(self, f: Fn[T, U], /) -> Option[U]:
281        """Map the inner value to another type. Returning `Some(None)` if `T` is `None`.
282
283        Example
284        -------
285        ```py
286        value = Some(5.0)
287
288        print(value.map(lambda x: x * 2.0))
289        # Some(10.0)
290
291        value: Option[bool] = Some(None)
292        print(value)
293        # Some(None)
294        ```
295        """
296        if self._value is None:
297            return NOTHING
298
299        return Some(f(self._value))
300
301    def map_or(self, default: U, f: Fn[T, U], /) -> U:
302        """Map the inner value to another type or return `default` if its `None`.
303
304        Example
305        -------
306        ```py
307        value: Option[float] = Some(5.0)
308
309        # map to int.
310        print(value.map_or(0, int))
311        # 6
312
313        value: Option[float] = Some(None)
314        print(value.map_or(0, int)
315        # 0
316        ```
317        """
318        if self._value is None:
319            return default
320
321        return f(self._value)
322
323    def map_or_else(self, default: FnOnce[U], f: Fn[T, U], /) -> U:
324        """Map the inner value to another type, or return `default()` if its `None`.
325
326        Example
327        -------
328        ```py
329        def default() -> int:
330            return sys.getsizeof(object())
331
332        value: Option[float] = Some(5.0)
333
334        # map to int.
335        print(value.map_or_else(default, int))
336        # 6
337
338        value: Option[float] = Some(None)
339        print(value.map_or_else(default, int)
340        # 28 <- size of object()
341        ```
342        """
343        if self._value is None:
344            return default()
345
346        return f(self._value)
347
348    def filter(self, predicate: Fn[T, bool]) -> Option[T]:
349        """Returns `Some(None)` if the contained value is `None`,
350
351        otherwise calls the predicate and returns `Some(T)` if the predicate returns `True`.
352
353        Example
354        -------
355        ```py
356        value = Some([1, 2, 3])
357
358        print(value.filter(lambda x: 1 in x))
359        # Some([1, 2, 3])
360
361        value: Option[int] = Some([1, 2, 3]) # or Some(None)
362        print(value.filter(lambda x: 1 not in x))
363        # None
364        ```
365        """
366        if (value := self._value) is not None:
367            if predicate(value):
368                return Some(value)
369
370        return NOTHING
371
372    def ok_or(self, err: U) -> _result.Result[T, U]:
373        """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err)`.
374
375        Example
376        -------
377        ```py
378        xyz: Option[str] = Some("foo")
379        assert xyz.ok_or(None) == Ok("foo")
380
381        xyz: Option[str] = Some(None)
382        assert xyz.ok_or(None) == Err(None)
383        ```
384        """
385        if self._value is None:
386            return _result.Err(err)
387
388        return _result.Ok(self._value)
389
390    def ok_or_else(self, err: FnOnce[U]) -> _result.Result[T, U]:
391        """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err())`.
392
393        Example
394        -------
395        ```py
396        xyz: Option[str] = Some("foo")
397        assert xyz.ok_or(None) == Ok("foo")
398
399        xyz: Option[str] = Some(None)
400        assert xyz.ok_or(None) == Err(None)
401        ```
402        """
403        if self._value is None:
404            return _result.Err(err())
405
406        return _result.Ok(self._value)
407
408    def zip(self, other: Option[U]) -> Option[tuple[T, U]]:
409        """Zips `self` with `other`.
410
411        if `self` is `Some(s)` and other is `Some(o)`, this returns `Some((s, o))` otherwise `None`.
412
413        Example
414        -------
415        ```py
416        x = Some(1)
417        y = Some("hi")
418        z: Option[str] = Some(None)
419
420        assert x.zip(y) == Some((1, "hi"))
421        assert x.zip(z) == Some(None)
422        ```
423        """
424        if self._value is not None and other._value is not None:
425            return Some((self._value, other._value))
426
427        return NOTHING
428
429    def zip_with(
430        self, other: Option[U], f: collections.Callable[[T, U], T_co]
431    ) -> Option[T_co]:
432        """Zips `self` with `other` using function `f`.
433
434        if `self` is `Some(s)` and other is `Some(o)`, this returns `Some(f(s, o))` otherwise `None`.
435
436        Example
437        -------
438        ```py
439        @dataclass
440        class Point:
441            x: float
442            y: float
443
444        x, y = Some(32.1), Some(42.4)
445        assert x.zip_with(y, Point) == Some(Point(32.1, 42.4))
446        ```
447        """
448        if self._value is not None and other._value is not None:
449            return Some(f(self._value, other._value))
450
451        return NOTHING
452
453    # *- Inner operations *-
454
455    def take(self) -> Option[T]:
456        """Take the value from `self` Setting it to `None`, and then return `Some(v)`.
457
458        If you don't care about the original value, use `Option.clear()` instead.
459
460        Example
461        -------
462        ```py
463        original = Some("Hi")
464        new = original.take()
465
466        print(original, new)
467        # None, Some("Hi")
468        ```
469        """
470        if self._value is None:
471            return NOTHING
472
473        val = self._value
474        self._value = None
475        return Some(val)
476
477    def take_if(self, predicate: collections.Callable[[T], bool]) -> Option[T]:
478        """Take the value from `Self`, Setting it to `None` only if predicate returns `True`.
479
480        If you don't care about the original value, use `Option.clear_if()` instead.
481
482        Example
483        -------
484        ```py
485        def validate(email: str) -> bool:
486            # you can obviously validate this better.
487            return email.find('@') == 1
488
489        original = Some("flex@gg.com")
490        valid = original.take_if(validate)
491        assert is_allowed.is_some() and original.is_none()
492
493        original = Some("mail.example.com")
494        invalid = original.take_if(validate)
495        assert invalid.is_none() and original.is_some()
496        ```
497        """
498        if self.map_or(False, predicate):
499            return self.take()
500
501        return NOTHING
502
503    def clear(self) -> None:
504        """Clear the inner value, setting it to `None`.
505
506        If you care about the original value, use `Option.take()` instead.
507
508        Example
509        -------
510        ```py
511        value = Some("Hello")
512        value.clear()
513        assert value.is_none()
514        ```
515        """
516        self._value = None
517
518    def clear_if(self, predicate: Fn[T, bool]) -> None:
519        """Clear the inner value, setting it to `None` if the predicate returns `True`.
520
521        If you care about the original value, use `Option.take_if()` instead.
522
523        Example
524        -------
525        ```py
526        value = Some("Hello")
527        value.clear_if(lambda x: x == "Hello")
528        assert value.is_none()
529        ```
530        """
531        if self._value is not None and predicate(self._value):
532            self._value = None
533
534    def replace(self, value: T) -> Option[T]:
535        """Replace the contained value with another value.
536
537        Use `Option.insert` if you want to return the original value
538        that got inserted instead of `self`
539
540        Example
541        -------
542        ```py
543        value: Option[str] = Some(None)
544        value.replace("Hello")
545        # Some("Hello")
546        ```
547        """
548        self._value = value
549        return self
550
551    def insert(self, value: T) -> T:
552        """Insert a value into the option, and then return a reference to it.
553
554        This will overwrite the old value if it was already contained.
555
556        Example
557        -------
558        ```py
559        flag: Option[bool] = Some(None)
560        flag_ref = flag.insert(True)
561        assert flag_ref == True
562        assert flag.unwrap() == True
563        ```
564        """
565        self._value = value
566        return value
567
568    def get_or_insert(self, value: T) -> T:
569        """Insert a value into the option if it was `None`,
570        and then return a reference to it.
571
572        Example
573        -------
574        ```py
575        state: Option[bool] = Some(None)
576        assert state.get_or_insert(True) is True
577        assert state.get_or_insert(False) is True
578        ```
579        """
580        if self._value is not None:
581            return self._value
582
583        self._value = value
584        return value
585
586    def get_or_insert_with(self, f: FnOnce[T]) -> T:
587        """Insert a value into the option computed from `f()` if it was `None`,
588        and then return a reference to it.
589
590        Example
591        -------
592        ```py
593        flag: Option[bool] = Some(None)
594        flag_ref = flag.insert(True)
595        assert flag_ref == True
596        assert flag.unwrap() == True
597        ```
598        """
599        if self._value is not None:
600            return self._value
601
602        v = self._value = f()
603        return v
604
605    def and_ok(self, optb: Option[T]) -> Option[T]:
606        """Returns `None` if `self` or `optb` is `None`, otherwise return `optb`.
607
608        aliases: `Option::and`
609
610        Example
611        -------
612        ```py
613        x = Some(1)
614        y: Option[str] = Some(None)
615        assert x.and_ok(y) == Some(None)
616
617        x: Option[str] = Some(None)
618        y = Some(1)
619        assert x.and_ok(y) == Some(None)
620
621        x: Option[str] = Some("hi")
622        y = Some(100)
623        assert x.and_ok(y) == Some(100)
624        ```
625        """
626        if self._value is None or optb._value is None:
627            return optb
628
629        return NOTHING
630
631    def and_then(self, f: Fn[T, Option[T]]) -> Option[T]:
632        """Returns `Some(None)` if the contained value is `None`, otherwise call `f()`
633        on `T` and return `Option[T]`.
634
635        Example
636        -------
637        ```py
638        value = Some(5)
639        print(value.and_then(lambda x: Some(x * 2)))
640        # Some(10)
641
642        value: Option[int] = Some(None)
643        print(value.and_then(lambda x: Some(x * 2)))
644        # Some(None)
645        ```
646        """
647        if self._value is None:
648            return NOTHING
649
650        return f(self._value)
651
652    def inspect(self, f: Fn[T, typing.Any]) -> Option[T]:
653        """Calls `f()` on the contained value if it was `Some(v)`, otherwise does nothing.
654
655        Example
656        -------
657        ```py
658        def debug(x: str) -> None:
659            print("Debugging:", x)
660
661        value = Some("foo")
662        inner = value.inspect(debug).expect("no value to debug")
663        # prints: Debugging: "foo"
664
665        value: Option[str] = Some(None)
666        value.inspect(debug) # prints nothing
667        """
668        if self._value is not None:
669            f(self._value)
670
671        return self
672
673    # *- Builder methods *-
674
675    def iter(self) -> _iter.ExactSizeIterator[T]:
676        """Returns an iterator over the contained value.
677
678        Example
679        -------
680        ```py
681        from sain import Some
682        value = Some("gg").iter()
683        assert value.next() == Some("gg")
684
685        value: Option[int] = Some(None)
686        assert value.iter().next().is_none()
687        ```
688        """
689        if self._value is None:
690            return _iter.empty()
691
692        return _iter.once(self._value)
693
694    # *- Boolean checks *-
695
696    def is_some(self) -> bool:
697        """Returns `True` if the contained value is not `None`, otherwise returns `False`.
698
699        Example
700        -------
701        ```py
702        value = Some(5)
703        print(value.is_some())
704        # True
705
706        value: Option[int] = Some(None)
707        print(value.is_some())
708        # False
709        ```
710        """
711        return self._value is not None
712
713    def is_some_and(self, predicate: Fn[T, bool]) -> bool:
714        """Returns `True` if the contained value is not `None` and
715        the predicate returns `True`, otherwise returns `False`.
716
717        Example
718        -------
719        ```py
720        value = Some(5)
721        print(value.is_some_and(lambda x: x > 3))
722        # True
723
724        value: Option[int] = Some(None)
725        print(value.is_some_and(lambda x: x > 3))
726        # False
727        ```
728        """
729        return self._value is not None and predicate(self._value)
730
731    def is_none(self) -> bool:
732        """Returns `True` if the contained value is `None`, otherwise returns `False`.
733
734        Example
735        -------
736        ```py
737        value = Some(5)
738        print(value.is_none())
739        # False
740
741        value: Option[int] = Some(None)
742        print(value.is_none())
743        # True
744        ```
745        """
746        return self._value is None
747
748    def is_none_or(self, f: Fn[T, bool]) -> bool:
749        """Returns `True` if the contained value is `None` or the predicate returns `True`,
750        otherwise returns `False`.
751
752        Example
753        -------
754        ```py
755        value = Some(5)
756        print(value.is_none_or(lambda x: x > 3))
757        # False
758
759        value: Option[int] = Some(None)
760        print(value.is_none_or(lambda x: x > 3))
761        # True
762        ```
763        """
764        match self._value:
765            case None:
766                return True
767            case x:
768                return f(x)
769
770    def __repr__(self) -> str:
771        if self._value is None:
772            return "None"
773        return f"Some({self._value!r})"
774
775    __str__ = __repr__
776
777    def __invert__(self) -> T:
778        return self.unwrap()
779
780    def __or__(self, other: T) -> T:
781        return self._value if self._value is not None else other
782
783    def __bool__(self) -> bool:
784        return self._value is not None
785
786    def __eq__(self, other: None | object) -> bool:
787        if other is None:
788            return self._value is None
789
790        if not isinstance(other, Some):
791            return NotImplemented
792
793        return self._value == other._value  # pyright: ignore[reportUnknownVariableType, reportUnknownMemberType]
794
795    def __ne__(self, other: object) -> bool:
796        return not self.__eq__(other)
797
798    def __hash__(self) -> int:
799        return hash(self._value)

An interface for an object that has a default value.

Example
from sain import Default

class Cache(Default[dict[str, Any]]):

    @staticmethod
    def default() -> dict[str, Any]:
        return {}

cache = Cache.default()
print(cache)
assert isinstance(cache, Default)
# {}

Implementations

This class implements Default in Rust.

Some(value: 'T | None', /)
137    def __init__(self, value: T | None, /) -> None:
138        self._value = value
@staticmethod
def default() -> 'Option[T]':
140    @staticmethod
141    def default() -> Option[T]:
142        """Default value for `Option<T>`. Returns `None` wrapped in `Some`.
143
144        Example
145        -------
146        ```py
147        assert Some[int].default() is NOTHING
148        ```
149        """
150        return NOTHING

Return the default value of the object.

def transpose(self) -> 'T | None':
154    def transpose(self) -> T | None:
155        """Convert `Option[T]` into `T | None`.
156
157        Examples
158        --------
159        ```py
160        opt = Some('char')
161        x = opt.transpose()
162        assert x == 'char'
163
164        opt = Some(None)
165        assert opt.transpose() is None
166        ```
167        """
168        return self._value

Convert Option[T] into T | None.

Examples
opt = Some('char')
x = opt.transpose()
assert x == 'char'

opt = Some(None)
assert opt.transpose() is None
def unwrap(self, /) -> 'T':
170    def unwrap(self) -> T:
171        """Unwrap the inner value either returning if its not `None` or raising a `RuntimeError`.
172
173        It's usually not recommended to use this method in production code, and instead use safer options such as `unwrap_or` or match patterns.
174
175        Example
176        -------
177        ```py
178        value = Some(5)
179        print(value.unwrap())
180        # 5
181
182        value = Some(None)
183        print(value.unwrap())
184        # RuntimeError
185        ```
186
187        Raises
188        ------
189        `RuntimeError`
190            If the inner value is `None`.
191        """
192        if self._value is None:
193            raise RuntimeError("Called `Option.unwrap()` on `None`.") from None
194
195        return self._value

Unwrap the inner value either returning if its not None or raising a RuntimeError.

It's usually not recommended to use this method in production code, and instead use safer options such as unwrap_or or match patterns.

Example
value = Some(5)
print(value.unwrap())
# 5

value = Some(None)
print(value.unwrap())
# RuntimeError
Raises
  • RuntimeError: If the inner value is None.
def unwrap_or(self, default: 'T') -> 'T':
197    def unwrap_or(self, default: T, /) -> T:
198        """Unwrap the inner value either returning if its not `None` or returning `default`.
199
200        Example
201        -------
202        ```py
203        value = Some(5)
204        print(value.unwrap_or(10))
205        # 5
206
207        # Type hint is required here.
208        value: Option[int] = Some(None)
209        print(value.unwrap_or(10))
210        # 10
211        ```
212        """
213        if self._value is None:
214            return default
215
216        return self._value

Unwrap the inner value either returning if its not None or returning default.

Example
value = Some(5)
print(value.unwrap_or(10))
# 5

# Type hint is required here.
value: Option[int] = Some(None)
print(value.unwrap_or(10))
# 10
def unwrap_or_else(self, f: 'FnOnce[T]') -> 'T':
218    def unwrap_or_else(self, f: FnOnce[T], /) -> T:
219        """Unwrap the inner value either returning if its not `None` or calling `f` to get a default value.
220
221        Example
222        -------
223        ```py
224        value = Some(5)
225        print(value.unwrap_or_else(lambda: 10))
226        # 5
227
228        value: Option[bool] = Some(None)
229        print(value.unwrap_or_else(lambda: True))
230        # True
231        ```
232        """
233        if self._value is None:
234            return f()
235
236        return self._value

Unwrap the inner value either returning if its not None or calling f to get a default value.

Example
value = Some(5)
print(value.unwrap_or_else(lambda: 10))
# 5

value: Option[bool] = Some(None)
print(value.unwrap_or_else(lambda: True))
# True
@macros.unsafe
def unwrap_unchecked(self) -> 'T':
238    @macros.unsafe
239    def unwrap_unchecked(self) -> T:
240        """Returns the contained Some value without checking that the value is not None.
241
242        Example
243        -------
244        ```py
245        v: Option[float] = Some(1.2)
246        v.unwrap_unchecked() # 1.2
247
248        v: Option[float] = Some(None)
249        print(v.unwrap_unchecked()) # Undefined Behavior
250        ```
251        """
252        #! SAFETY: The caller guarantees that the value is not None.
253        return self._value  # pyright: ignore

Returns the contained Some value without checking that the value is not None.

Example
v: Option[float] = Some(1.2)
v.unwrap_unchecked() # 1.2

v: Option[float] = Some(None)
print(v.unwrap_unchecked()) # Undefined Behavior

Safety ⚠️

Calling this method without knowing the output is considered undefined behavior.

def expect(self, message: str) -> 'T':
255    def expect(self, message: str, /) -> T:
256        """Returns the contained `Some` value.
257
258        raises if the value is `None` with a custom provided `message`.
259
260        Example
261        -------
262        ```py
263        value = Some("Hello")
264
265        print(value.expect("Value is None"))
266        # "Hello"
267
268        value: Option[str] = Some(None)
269        print(value.expect("Value is None"))
270        # RuntimeError("Value is None")
271        ```
272        """
273        if self._value is None:
274            raise RuntimeError(message)
275
276        return self._value

Returns the contained Some value.

raises if the value is None with a custom provided message.

Example
value = Some("Hello")

print(value.expect("Value is None"))
# "Hello"

value: Option[str] = Some(None)
print(value.expect("Value is None"))
# RuntimeError("Value is None")
def map(self, f: 'Fn[T, U]') -> 'Option[U]':
280    def map(self, f: Fn[T, U], /) -> Option[U]:
281        """Map the inner value to another type. Returning `Some(None)` if `T` is `None`.
282
283        Example
284        -------
285        ```py
286        value = Some(5.0)
287
288        print(value.map(lambda x: x * 2.0))
289        # Some(10.0)
290
291        value: Option[bool] = Some(None)
292        print(value)
293        # Some(None)
294        ```
295        """
296        if self._value is None:
297            return NOTHING
298
299        return Some(f(self._value))

Map the inner value to another type. Returning Some(None) if T is None.

Example
value = Some(5.0)

print(value.map(lambda x: x * 2.0))
# Some(10.0)

value: Option[bool] = Some(None)
print(value)
# Some(None)
def map_or(self, default: 'U', f: 'Fn[T, U]') -> 'U':
301    def map_or(self, default: U, f: Fn[T, U], /) -> U:
302        """Map the inner value to another type or return `default` if its `None`.
303
304        Example
305        -------
306        ```py
307        value: Option[float] = Some(5.0)
308
309        # map to int.
310        print(value.map_or(0, int))
311        # 6
312
313        value: Option[float] = Some(None)
314        print(value.map_or(0, int)
315        # 0
316        ```
317        """
318        if self._value is None:
319            return default
320
321        return f(self._value)

Map the inner value to another type or return default if its None.

Example
value: Option[float] = Some(5.0)

# map to int.
print(value.map_or(0, int))
# 6

value: Option[float] = Some(None)
print(value.map_or(0, int)
# 0
def map_or_else(self, default: 'FnOnce[U]', f: 'Fn[T, U]') -> 'U':
323    def map_or_else(self, default: FnOnce[U], f: Fn[T, U], /) -> U:
324        """Map the inner value to another type, or return `default()` if its `None`.
325
326        Example
327        -------
328        ```py
329        def default() -> int:
330            return sys.getsizeof(object())
331
332        value: Option[float] = Some(5.0)
333
334        # map to int.
335        print(value.map_or_else(default, int))
336        # 6
337
338        value: Option[float] = Some(None)
339        print(value.map_or_else(default, int)
340        # 28 <- size of object()
341        ```
342        """
343        if self._value is None:
344            return default()
345
346        return f(self._value)

Map the inner value to another type, or return default() if its None.

Example
def default() -> int:
    return sys.getsizeof(object())

value: Option[float] = Some(5.0)

# map to int.
print(value.map_or_else(default, int))
# 6

value: Option[float] = Some(None)
print(value.map_or_else(default, int)
# 28 <- size of object()
def filter(self, predicate: 'Fn[T, bool]') -> 'Option[T]':
348    def filter(self, predicate: Fn[T, bool]) -> Option[T]:
349        """Returns `Some(None)` if the contained value is `None`,
350
351        otherwise calls the predicate and returns `Some(T)` if the predicate returns `True`.
352
353        Example
354        -------
355        ```py
356        value = Some([1, 2, 3])
357
358        print(value.filter(lambda x: 1 in x))
359        # Some([1, 2, 3])
360
361        value: Option[int] = Some([1, 2, 3]) # or Some(None)
362        print(value.filter(lambda x: 1 not in x))
363        # None
364        ```
365        """
366        if (value := self._value) is not None:
367            if predicate(value):
368                return Some(value)
369
370        return NOTHING

Returns Some(None) if the contained value is None,

otherwise calls the predicate and returns Some(T) if the predicate returns True.

Example
value = Some([1, 2, 3])

print(value.filter(lambda x: 1 in x))
# Some([1, 2, 3])

value: Option[int] = Some([1, 2, 3]) # or Some(None)
print(value.filter(lambda x: 1 not in x))
# None
def ok_or(self, err: 'E') -> 'Result[T, E]':
372    def ok_or(self, err: U) -> _result.Result[T, U]:
373        """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err)`.
374
375        Example
376        -------
377        ```py
378        xyz: Option[str] = Some("foo")
379        assert xyz.ok_or(None) == Ok("foo")
380
381        xyz: Option[str] = Some(None)
382        assert xyz.ok_or(None) == Err(None)
383        ```
384        """
385        if self._value is None:
386            return _result.Err(err)
387
388        return _result.Ok(self._value)

Transforms the Option<T> into a Result<T, E>, mapping Some(v) to Ok(v) and None to Err(err).

Example
xyz: Option[str] = Some("foo")
assert xyz.ok_or(None) == Ok("foo")

xyz: Option[str] = Some(None)
assert xyz.ok_or(None) == Err(None)
def ok_or_else(self, err: 'FnOnce[E]') -> 'Result[T, E]':
390    def ok_or_else(self, err: FnOnce[U]) -> _result.Result[T, U]:
391        """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err())`.
392
393        Example
394        -------
395        ```py
396        xyz: Option[str] = Some("foo")
397        assert xyz.ok_or(None) == Ok("foo")
398
399        xyz: Option[str] = Some(None)
400        assert xyz.ok_or(None) == Err(None)
401        ```
402        """
403        if self._value is None:
404            return _result.Err(err())
405
406        return _result.Ok(self._value)

Transforms the Option<T> into a Result<T, E>, mapping Some(v) to Ok(v) and None to Err(err()).

Example
xyz: Option[str] = Some("foo")
assert xyz.ok_or(None) == Ok("foo")

xyz: Option[str] = Some(None)
assert xyz.ok_or(None) == Err(None)
def zip(self, other: 'Option[U]') -> 'Option[tuple[T, U]]':
408    def zip(self, other: Option[U]) -> Option[tuple[T, U]]:
409        """Zips `self` with `other`.
410
411        if `self` is `Some(s)` and other is `Some(o)`, this returns `Some((s, o))` otherwise `None`.
412
413        Example
414        -------
415        ```py
416        x = Some(1)
417        y = Some("hi")
418        z: Option[str] = Some(None)
419
420        assert x.zip(y) == Some((1, "hi"))
421        assert x.zip(z) == Some(None)
422        ```
423        """
424        if self._value is not None and other._value is not None:
425            return Some((self._value, other._value))
426
427        return NOTHING

Zips self with other.

if self is Some(s) and other is Some(o), this returns Some((s, o)) otherwise None.

Example
x = Some(1)
y = Some("hi")
z: Option[str] = Some(None)

assert x.zip(y) == Some((1, "hi"))
assert x.zip(z) == Some(None)
def zip_with( self, other: 'Option[U]', f: '_collections.Callable[[T, U], R]') -> 'Option[R]':
429    def zip_with(
430        self, other: Option[U], f: collections.Callable[[T, U], T_co]
431    ) -> Option[T_co]:
432        """Zips `self` with `other` using function `f`.
433
434        if `self` is `Some(s)` and other is `Some(o)`, this returns `Some(f(s, o))` otherwise `None`.
435
436        Example
437        -------
438        ```py
439        @dataclass
440        class Point:
441            x: float
442            y: float
443
444        x, y = Some(32.1), Some(42.4)
445        assert x.zip_with(y, Point) == Some(Point(32.1, 42.4))
446        ```
447        """
448        if self._value is not None and other._value is not None:
449            return Some(f(self._value, other._value))
450
451        return NOTHING

Zips self with other using function f.

if self is Some(s) and other is Some(o), this returns Some(f(s, o)) otherwise None.

Example
@dataclass
class Point:
    x: float
    y: float

x, y = Some(32.1), Some(42.4)
assert x.zip_with(y, Point) == Some(Point(32.1, 42.4))
def take(self) -> 'Option[T]':
455    def take(self) -> Option[T]:
456        """Take the value from `self` Setting it to `None`, and then return `Some(v)`.
457
458        If you don't care about the original value, use `Option.clear()` instead.
459
460        Example
461        -------
462        ```py
463        original = Some("Hi")
464        new = original.take()
465
466        print(original, new)
467        # None, Some("Hi")
468        ```
469        """
470        if self._value is None:
471            return NOTHING
472
473        val = self._value
474        self._value = None
475        return Some(val)

Take the value from self Setting it to None, and then return Some(v).

If you don't care about the original value, use Option.clear() instead.

Example
original = Some("Hi")
new = original.take()

print(original, new)
# None, Some("Hi")
def take_if(self, predicate: 'Fn[T, bool]') -> 'Option[T]':
477    def take_if(self, predicate: collections.Callable[[T], bool]) -> Option[T]:
478        """Take the value from `Self`, Setting it to `None` only if predicate returns `True`.
479
480        If you don't care about the original value, use `Option.clear_if()` instead.
481
482        Example
483        -------
484        ```py
485        def validate(email: str) -> bool:
486            # you can obviously validate this better.
487            return email.find('@') == 1
488
489        original = Some("flex@gg.com")
490        valid = original.take_if(validate)
491        assert is_allowed.is_some() and original.is_none()
492
493        original = Some("mail.example.com")
494        invalid = original.take_if(validate)
495        assert invalid.is_none() and original.is_some()
496        ```
497        """
498        if self.map_or(False, predicate):
499            return self.take()
500
501        return NOTHING

Take the value from Self, Setting it to None only if predicate returns True.

If you don't care about the original value, use Option.clear_if() instead.

Example
def validate(email: str) -> bool:
    # you can obviously validate this better.
    return email.find('@') == 1

original = Some("flex@gg.com")
valid = original.take_if(validate)
assert is_allowed.is_some() and original.is_none()

original = Some("mail.example.com")
invalid = original.take_if(validate)
assert invalid.is_none() and original.is_some()
def clear(self) -> None:
503    def clear(self) -> None:
504        """Clear the inner value, setting it to `None`.
505
506        If you care about the original value, use `Option.take()` instead.
507
508        Example
509        -------
510        ```py
511        value = Some("Hello")
512        value.clear()
513        assert value.is_none()
514        ```
515        """
516        self._value = None

Clear the inner value, setting it to None.

If you care about the original value, use Option.take() instead.

Example
value = Some("Hello")
value.clear()
assert value.is_none()
def clear_if(self, predicate: 'Fn[T, bool]') -> None:
518    def clear_if(self, predicate: Fn[T, bool]) -> None:
519        """Clear the inner value, setting it to `None` if the predicate returns `True`.
520
521        If you care about the original value, use `Option.take_if()` instead.
522
523        Example
524        -------
525        ```py
526        value = Some("Hello")
527        value.clear_if(lambda x: x == "Hello")
528        assert value.is_none()
529        ```
530        """
531        if self._value is not None and predicate(self._value):
532            self._value = None

Clear the inner value, setting it to None if the predicate returns True.

If you care about the original value, use Option.take_if() instead.

Example
value = Some("Hello")
value.clear_if(lambda x: x == "Hello")
assert value.is_none()
def replace(self, value: 'T') -> 'Option[T]':
534    def replace(self, value: T) -> Option[T]:
535        """Replace the contained value with another value.
536
537        Use `Option.insert` if you want to return the original value
538        that got inserted instead of `self`
539
540        Example
541        -------
542        ```py
543        value: Option[str] = Some(None)
544        value.replace("Hello")
545        # Some("Hello")
546        ```
547        """
548        self._value = value
549        return self

Replace the contained value with another value.

Use Option.insert if you want to return the original value that got inserted instead of self

Example
value: Option[str] = Some(None)
value.replace("Hello")
# Some("Hello")
def insert(self, value: 'T') -> 'T':
551    def insert(self, value: T) -> T:
552        """Insert a value into the option, and then return a reference to it.
553
554        This will overwrite the old value if it was already contained.
555
556        Example
557        -------
558        ```py
559        flag: Option[bool] = Some(None)
560        flag_ref = flag.insert(True)
561        assert flag_ref == True
562        assert flag.unwrap() == True
563        ```
564        """
565        self._value = value
566        return value

Insert a value into the option, and then return a reference to it.

This will overwrite the old value if it was already contained.

Example
flag: Option[bool] = Some(None)
flag_ref = flag.insert(True)
assert flag_ref == True
assert flag.unwrap() == True
def get_or_insert(self, value: 'T') -> 'T':
568    def get_or_insert(self, value: T) -> T:
569        """Insert a value into the option if it was `None`,
570        and then return a reference to it.
571
572        Example
573        -------
574        ```py
575        state: Option[bool] = Some(None)
576        assert state.get_or_insert(True) is True
577        assert state.get_or_insert(False) is True
578        ```
579        """
580        if self._value is not None:
581            return self._value
582
583        self._value = value
584        return value

Insert a value into the option if it was None, and then return a reference to it.

Example
state: Option[bool] = Some(None)
assert state.get_or_insert(True) is True
assert state.get_or_insert(False) is True
def get_or_insert_with(self, f: 'FnOnce[T]') -> 'T':
586    def get_or_insert_with(self, f: FnOnce[T]) -> T:
587        """Insert a value into the option computed from `f()` if it was `None`,
588        and then return a reference to it.
589
590        Example
591        -------
592        ```py
593        flag: Option[bool] = Some(None)
594        flag_ref = flag.insert(True)
595        assert flag_ref == True
596        assert flag.unwrap() == True
597        ```
598        """
599        if self._value is not None:
600            return self._value
601
602        v = self._value = f()
603        return v

Insert a value into the option computed from f() if it was None, and then return a reference to it.

Example
flag: Option[bool] = Some(None)
flag_ref = flag.insert(True)
assert flag_ref == True
assert flag.unwrap() == True
def and_ok(self, optb: 'Option[T]') -> 'Option[U]':
605    def and_ok(self, optb: Option[T]) -> Option[T]:
606        """Returns `None` if `self` or `optb` is `None`, otherwise return `optb`.
607
608        aliases: `Option::and`
609
610        Example
611        -------
612        ```py
613        x = Some(1)
614        y: Option[str] = Some(None)
615        assert x.and_ok(y) == Some(None)
616
617        x: Option[str] = Some(None)
618        y = Some(1)
619        assert x.and_ok(y) == Some(None)
620
621        x: Option[str] = Some("hi")
622        y = Some(100)
623        assert x.and_ok(y) == Some(100)
624        ```
625        """
626        if self._value is None or optb._value is None:
627            return optb
628
629        return NOTHING

Returns None if self or optb is None, otherwise return optb.

aliases: Option::and

Example
x = Some(1)
y: Option[str] = Some(None)
assert x.and_ok(y) == Some(None)

x: Option[str] = Some(None)
y = Some(1)
assert x.and_ok(y) == Some(None)

x: Option[str] = Some("hi")
y = Some(100)
assert x.and_ok(y) == Some(100)
def and_then(self, f: 'Fn[T, Option[U]]') -> 'Option[U]':
631    def and_then(self, f: Fn[T, Option[T]]) -> Option[T]:
632        """Returns `Some(None)` if the contained value is `None`, otherwise call `f()`
633        on `T` and return `Option[T]`.
634
635        Example
636        -------
637        ```py
638        value = Some(5)
639        print(value.and_then(lambda x: Some(x * 2)))
640        # Some(10)
641
642        value: Option[int] = Some(None)
643        print(value.and_then(lambda x: Some(x * 2)))
644        # Some(None)
645        ```
646        """
647        if self._value is None:
648            return NOTHING
649
650        return f(self._value)

Returns Some(None) if the contained value is None, otherwise call f() on T and return Option[T].

Example
value = Some(5)
print(value.and_then(lambda x: Some(x * 2)))
# Some(10)

value: Option[int] = Some(None)
print(value.and_then(lambda x: Some(x * 2)))
# Some(None)
def inspect(self, f: 'Fn[T, _typing.Any]') -> 'Option[T]':
652    def inspect(self, f: Fn[T, typing.Any]) -> Option[T]:
653        """Calls `f()` on the contained value if it was `Some(v)`, otherwise does nothing.
654
655        Example
656        -------
657        ```py
658        def debug(x: str) -> None:
659            print("Debugging:", x)
660
661        value = Some("foo")
662        inner = value.inspect(debug).expect("no value to debug")
663        # prints: Debugging: "foo"
664
665        value: Option[str] = Some(None)
666        value.inspect(debug) # prints nothing
667        """
668        if self._value is not None:
669            f(self._value)
670
671        return self

Calls f() on the contained value if it was Some(v), otherwise does nothing.

Example

```py def debug(x: str) -> None: print("Debugging:", x)

value = Some("foo") inner = value.inspect(debug).expect("no value to debug")

prints: Debugging: "foo"

value: Option[str] = Some(None) value.inspect(debug) # prints nothing

def iter(self) -> 'ExactSizeIterator[T]':
675    def iter(self) -> _iter.ExactSizeIterator[T]:
676        """Returns an iterator over the contained value.
677
678        Example
679        -------
680        ```py
681        from sain import Some
682        value = Some("gg").iter()
683        assert value.next() == Some("gg")
684
685        value: Option[int] = Some(None)
686        assert value.iter().next().is_none()
687        ```
688        """
689        if self._value is None:
690            return _iter.empty()
691
692        return _iter.once(self._value)

Returns an iterator over the contained value.

Example
from sain import Some
value = Some("gg")sain.iter()
assert value.next() == Some("gg")

value: Option[int] = Some(None)
assert value.iter().next().is_none()
def is_some(self) -> bool:
696    def is_some(self) -> bool:
697        """Returns `True` if the contained value is not `None`, otherwise returns `False`.
698
699        Example
700        -------
701        ```py
702        value = Some(5)
703        print(value.is_some())
704        # True
705
706        value: Option[int] = Some(None)
707        print(value.is_some())
708        # False
709        ```
710        """
711        return self._value is not None

Returns True if the contained value is not None, otherwise returns False.

Example
value = Some(5)
print(value.is_some())
# True

value: Option[int] = Some(None)
print(value.is_some())
# False
def is_some_and(self, predicate: 'Fn[T, bool]') -> bool:
713    def is_some_and(self, predicate: Fn[T, bool]) -> bool:
714        """Returns `True` if the contained value is not `None` and
715        the predicate returns `True`, otherwise returns `False`.
716
717        Example
718        -------
719        ```py
720        value = Some(5)
721        print(value.is_some_and(lambda x: x > 3))
722        # True
723
724        value: Option[int] = Some(None)
725        print(value.is_some_and(lambda x: x > 3))
726        # False
727        ```
728        """
729        return self._value is not None and predicate(self._value)

Returns True if the contained value is not None and the predicate returns True, otherwise returns False.

Example
value = Some(5)
print(value.is_some_and(lambda x: x > 3))
# True

value: Option[int] = Some(None)
print(value.is_some_and(lambda x: x > 3))
# False
def is_none(self) -> bool:
731    def is_none(self) -> bool:
732        """Returns `True` if the contained value is `None`, otherwise returns `False`.
733
734        Example
735        -------
736        ```py
737        value = Some(5)
738        print(value.is_none())
739        # False
740
741        value: Option[int] = Some(None)
742        print(value.is_none())
743        # True
744        ```
745        """
746        return self._value is None

Returns True if the contained value is None, otherwise returns False.

Example
value = Some(5)
print(value.is_none())
# False

value: Option[int] = Some(None)
print(value.is_none())
# True
def is_none_or(self, f: 'Fn[T, bool]') -> bool:
748    def is_none_or(self, f: Fn[T, bool]) -> bool:
749        """Returns `True` if the contained value is `None` or the predicate returns `True`,
750        otherwise returns `False`.
751
752        Example
753        -------
754        ```py
755        value = Some(5)
756        print(value.is_none_or(lambda x: x > 3))
757        # False
758
759        value: Option[int] = Some(None)
760        print(value.is_none_or(lambda x: x > 3))
761        # True
762        ```
763        """
764        match self._value:
765            case None:
766                return True
767            case x:
768                return f(x)

Returns True if the contained value is None or the predicate returns True, otherwise returns False.

Example
value = Some(5)
print(value.is_none_or(lambda x: x > 3))
# False

value: Option[int] = Some(None)
print(value.is_none_or(lambda x: x > 3))
# True
Option = 'Some[T]'

A type hint for a value that can be Some<T> or None.

The reason this exist is to satisfy the UX with Rust's type system.

Example
from __future__ import annotations

import typing
from sain import Some

if typing.CHECKING:
    from sain import Option

foo: Option[str] = Some(None)
NOTHING: Option[typing.Any] = None

A constant that is always Some(None).

Example
from sain import NOTHING, Some

place_holder: Option[str] = NOTHING
assert NOTHING == Some(None) # True