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)
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.
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.
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
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 isNone.
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
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
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.
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")
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)
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
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()
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
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)
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)
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)
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))
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")
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()
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()
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()
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")
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
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
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
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)
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)
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
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()
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
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
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
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
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)
A constant that is always Some(None).
Example
from sain import NOTHING, Some
place_holder: Option[str] = NOTHING
assert NOTHING == Some(None) # True