Coverage for src / pyfplib / either.py: 42%
55 statements
« prev ^ index » next coverage.py v7.13.2, created at 2026-02-04 23:48 +0300
« prev ^ index » next coverage.py v7.13.2, created at 2026-02-04 23:48 +0300
1"""This module provides Either type and
2two constructors of Either: Left[L, R] and Right[L, R].
3"""
5from typing import Callable, Generic, Optional, TypeVar, Union, cast
7from pyfplib.option import Option
8from pyfplib.result import Result, T
10L = TypeVar("L")
11R = TypeVar("R")
12U = TypeVar("U")
15class Either(Generic[L, R]):
16 """Either is a basic class."""
18 def __init__(self, *, left: Optional[L] = None, right: Optional[R] = None):
19 self.__left: Optional[L] = left
20 self.__right: Optional[R] = right
22 def is_left(self) -> bool:
23 """Returns True if left value is not None."""
24 return self.__left is not None
26 def is_right(self) -> bool:
27 """Returns True if right value is not None."""
28 return self.__right is not None
30 def if_lef(self, fn: Callable[[L], None]) -> "Either[L, R]":
31 """\
32 Calls fn function and returns itself.
33 The fn function will be called if the left value is not None.
34 """
35 if self.is_left():
36 fn(cast(L, self.__left))
37 return self
39 def if_right(self, fn: Callable[[R], None]) -> "Either[L, R]":
40 """\
41 Calls fn function and returns itself.
42 The fn function will be called if the right value is not None.
43 """
44 if self.is_right():
45 fn(cast(R, self.__right))
46 return self
48 def left(self) -> Option[L]:
49 """Returns left value as Option[L]."""
50 return Option.from_optional(self.__left)
52 def right(self) -> Option[R]:
53 """Returns right value as Option[R]."""
54 return Option.from_optional(self.__right)
56 def map_left(self, fn: Callable[[L], U]) -> "Either[U, R]":
57 """Maps an Either[L, R] to Either[U, R]."""
58 either: Either[U, R]
59 if self.is_left():
60 either = Left[U, R](fn(cast(L, self.__left)))
61 else:
62 either = Right[U, R](cast(R, self.__right))
63 return either
65 def map_right(self, fn: Callable[[R], U]) -> "Either[L, U]":
66 """Maps an Either[L, R] to Either[L, U]."""
67 either: Either[L, U]
68 if self.is_right():
69 either = Right[L, U](fn(cast(R, self.__right)))
70 else:
71 either = Left[L, U](cast(L, self.__left))
72 return either
74 @staticmethod
75 def from_result(result: Result) -> "Either[T, Exception]":
76 """Creates Either[T, Exception] from Result."""
77 either: Either
78 if result.is_ok():
79 either = Left[T, Exception](cast(T, result.ok().unwrap()))
80 else:
81 either = Right[T, Exception](cast(Exception, result.err().unwrap()))
82 return either
85class Left(Either[L, R]):
86 """\
87 This Either constructor creates a new instance of Either
88 with a left value.
89 """
91 def __init__(self, value: L):
92 super().__init__(left=value)
94 @property
95 def value(self) -> Union[L, R]:
96 """Getter returns left or right value."""
97 return cast(L, self.left())
100class Right(Either[L, R]):
101 """\
102 This Either constructor creates a new instance of Either
103 with a right value.
104 """
106 def __init__(self, value: R):
107 super().__init__(right=value)
109 @property
110 def value(self) -> Union[L, R]:
111 """Getter returns left or right value."""
112 return cast(R, self.right())
115__all__ = ("Either", "Left", "Right")