monadcontainers.monads

  1from typing import Any, Callable, Generic
  2from typing import List as ListType
  3from typing import Optional, Type, TypeVar, Union
  4
  5T = TypeVar("T")
  6U = TypeVar("U")
  7
  8
  9class Monad(Generic[T]):
 10    """
 11    Identity monad (also the parent class for all other monads).
 12
 13    No additional work is handled on function calls, apart from returning
 14    a new monad with an updated result.
 15
 16    Can be used like so:
 17
 18    ```python
 19    def make_loud(x:str) -> str:
 20        return x.upper()
 21
 22    x = Monad("hello world!") >> make_loud
 23
 24    print(x.value)
 25    # prints out "HELLO WORLD!"
 26    ```
 27    """
 28    def __init__(self: Any, value: T) -> None:
 29        """
 30        Initialise a monad with the given value
 31        """
 32        self.value: T = value
 33
 34    def bind(self, func) -> "Monad":
 35        """
 36        Method to apply function to value of Monad.
 37        Returns a monad with the updated value.
 38
 39        Example:
 40        ```python
 41        Monad(2).bind(lambda x: x+1) == Monad(3)
 42        ```
 43
 44        Can be aliased with `>>` symbol:
 45        ```python
 46        (Monad(2) >> (lambda x: x+1)) == Monad(3)
 47        """
 48        return Monad(func(self.value))
 49
 50    def __rshift__(self, other) -> "Monad":
 51        """
 52        Dunder method to alias bind into >>
 53        """
 54        return self.bind(other)
 55
 56    def unwrap(self) -> T:
 57        """
 58        Return only the value of the monad without wrapping
 59        it.
 60
 61        ```python
 62        Monad(4).unwrap() == 4
 63        ```
 64        """
 65        return self.value
 66
 67    def __str__(self) -> str:
 68        """
 69        String representation
 70        """
 71        return f"{self.__class__.__name__}({self.value})"
 72
 73    def __repr__(self) -> str:
 74        """
 75        For repls
 76        """
 77        return self.__str__()
 78
 79
 80class Maybe(Monad, Generic[T]):
 81    """
 82    Monad to handle None values.
 83
 84    Write functions as if they can't recieve None values.
 85    If Monad value is None, it will skip execution of function
 86    and remain as None.
 87
 88    None example:
 89    ```python
 90    Maybe(None) >> (lambda x: x + 4)  # will remain None
 91    ```
 92    """
 93    def bind(self, func: Callable) -> "Maybe":
 94        """
 95        Execute function on value, unless None
 96        (in which case return None monad)
 97        """
 98        if self.value is None:
 99            return Maybe(None)
100        return Maybe(func(self.value))
101
102
103class List(Monad, Generic[T]):
104    """
105    Monad to apply function to all items in list.
106
107    Write functions as if they act on a single value.
108
109    Example:
110    ```python
111    x = (
112        List([1, 3, 7])
113        >> (lambda x: x + 1)
114        >> (lambda x: x / 2)
115    )
116    # x will evaluate to List([1, 2, 4])
117    """
118    def __init__(self: Any, value: ListType[T]) -> None:
119        """
120        Initialise a monad with the given list
121        """
122        self.value: ListType[T] = value
123
124    def bind(self, func: Callable) -> "List":
125        """
126        Map function on every element of list:
127
128        ```python
129        def make_exciting(text: str) -> str:
130            return test.upper() + "!!!"
131
132        fun_stuff = (
133            List(["hats", "cats", "bats"])
134            >> make_exciting
135        ).unwrap()
136
137        fun_stuff == ["HATS!!!", "CATS!!!", "BATS!!!"]
138        """
139        return List([func(x) for x in self.value])
140
141    def filter(self, func: Callable) -> "List":
142        """
143        Filter list to only elements with true return:
144
145        ```python
146        x = (
147            List([1, 2, 3, 4])
148            .filter(lambda x: x % 2 == 0)
149        ).unwrap()
150
151        x == [2, 4]
152        """
153        return List([i for i in self.value if func(i)])
154
155    def unwrap(self) -> ListType[T]:
156        """
157        Return only the value of the monad without wrapping
158        it.
159
160        ```python
161        List([4]).unwrap() == [4]
162        ```
163        """
164        return self.value
165
166
167class Result(Monad, Generic[T]):
168    """
169    Monad to handle errors. Handle exceptions on unwrap:
170
171    ```python
172    x = (
173        Result(3)
174        >> (lamba x / 0)
175    )
176
177    x.value == None
178    isinstance(x.exception, ZeroDivisionError)
179    x.unwrap_or(4) == 4
180    ```
181    """
182    def __init__(self, value: T, exception: Optional[Type[Exception]] = None):
183        """
184        Create Result monad with value and exception
185        (one of which will always be None)
186        """
187        self.value: T = value
188        self.exception = exception
189
190    def bind(self, func):
191        """
192        Execute function on value, if error is raised,
193        returned Monad will have value of "None" and an exception.
194
195        Otherwise, exception will be None, and value will be return.
196
197        If exception already exists, function won't be executed.
198        """
199        if self.exception:
200            return self
201        try:
202            return Result(func(self.value))
203        except Exception as exception:
204            return Result(None, exception)
205
206    def unwrap(self) -> T:
207        """
208        If exception is raised, will raise exception.
209        Otherwise will evaluate to value.
210        """
211        if self.exception:
212            raise self.exception
213        return self.value
214
215    def unwrap_or(self, value: U) -> Union[T, U]:
216        """
217        If exception is raised, will default to given value.
218        """
219        if self.exception:
220            return value
221        return self.value
222
223    def __str__(self) -> str:
224        """
225        Custom string representation
226        """
227        return f"{self.__class__.__name__}({self.exception or self.value})"
class Monad(typing.Generic[~T]):
10class Monad(Generic[T]):
11    """
12    Identity monad (also the parent class for all other monads).
13
14    No additional work is handled on function calls, apart from returning
15    a new monad with an updated result.
16
17    Can be used like so:
18
19    ```python
20    def make_loud(x:str) -> str:
21        return x.upper()
22
23    x = Monad("hello world!") >> make_loud
24
25    print(x.value)
26    # prints out "HELLO WORLD!"
27    ```
28    """
29    def __init__(self: Any, value: T) -> None:
30        """
31        Initialise a monad with the given value
32        """
33        self.value: T = value
34
35    def bind(self, func) -> "Monad":
36        """
37        Method to apply function to value of Monad.
38        Returns a monad with the updated value.
39
40        Example:
41        ```python
42        Monad(2).bind(lambda x: x+1) == Monad(3)
43        ```
44
45        Can be aliased with `>>` symbol:
46        ```python
47        (Monad(2) >> (lambda x: x+1)) == Monad(3)
48        """
49        return Monad(func(self.value))
50
51    def __rshift__(self, other) -> "Monad":
52        """
53        Dunder method to alias bind into >>
54        """
55        return self.bind(other)
56
57    def unwrap(self) -> T:
58        """
59        Return only the value of the monad without wrapping
60        it.
61
62        ```python
63        Monad(4).unwrap() == 4
64        ```
65        """
66        return self.value
67
68    def __str__(self) -> str:
69        """
70        String representation
71        """
72        return f"{self.__class__.__name__}({self.value})"
73
74    def __repr__(self) -> str:
75        """
76        For repls
77        """
78        return self.__str__()

Identity monad (also the parent class for all other monads).

No additional work is handled on function calls, apart from returning a new monad with an updated result.

Can be used like so:

def make_loud(x:str) -> str:
    return x.upper()

x = Monad("hello world!") >> make_loud

print(x.value)
# prints out "HELLO WORLD!"
Monad(value: ~T)
29    def __init__(self: Any, value: T) -> None:
30        """
31        Initialise a monad with the given value
32        """
33        self.value: T = value

Initialise a monad with the given value

value: ~T
def bind(self, func) -> Monad:
35    def bind(self, func) -> "Monad":
36        """
37        Method to apply function to value of Monad.
38        Returns a monad with the updated value.
39
40        Example:
41        ```python
42        Monad(2).bind(lambda x: x+1) == Monad(3)
43        ```
44
45        Can be aliased with `>>` symbol:
46        ```python
47        (Monad(2) >> (lambda x: x+1)) == Monad(3)
48        """
49        return Monad(func(self.value))

Method to apply function to value of Monad. Returns a monad with the updated value.

Example:

Monad(2).bind(lambda x: x+1) == Monad(3)

Can be aliased with >> symbol: ```python (Monad(2) >> (lambda x: x+1)) == Monad(3)

def unwrap(self) -> ~T:
57    def unwrap(self) -> T:
58        """
59        Return only the value of the monad without wrapping
60        it.
61
62        ```python
63        Monad(4).unwrap() == 4
64        ```
65        """
66        return self.value

Return only the value of the monad without wrapping it.

Monad(4).unwrap() == 4
class Maybe(Monad, typing.Generic[~T]):
 81class Maybe(Monad, Generic[T]):
 82    """
 83    Monad to handle None values.
 84
 85    Write functions as if they can't recieve None values.
 86    If Monad value is None, it will skip execution of function
 87    and remain as None.
 88
 89    None example:
 90    ```python
 91    Maybe(None) >> (lambda x: x + 4)  # will remain None
 92    ```
 93    """
 94    def bind(self, func: Callable) -> "Maybe":
 95        """
 96        Execute function on value, unless None
 97        (in which case return None monad)
 98        """
 99        if self.value is None:
100            return Maybe(None)
101        return Maybe(func(self.value))

Monad to handle None values.

Write functions as if they can't recieve None values. If Monad value is None, it will skip execution of function and remain as None.

None example:

Maybe(None) >> (lambda x: x + 4)  # will remain None
Inherited Members
Monad
Monad
value
bind
unwrap
class List(Monad, typing.Generic[~T]):
104class List(Monad, Generic[T]):
105    """
106    Monad to apply function to all items in list.
107
108    Write functions as if they act on a single value.
109
110    Example:
111    ```python
112    x = (
113        List([1, 3, 7])
114        >> (lambda x: x + 1)
115        >> (lambda x: x / 2)
116    )
117    # x will evaluate to List([1, 2, 4])
118    """
119    def __init__(self: Any, value: ListType[T]) -> None:
120        """
121        Initialise a monad with the given list
122        """
123        self.value: ListType[T] = value
124
125    def bind(self, func: Callable) -> "List":
126        """
127        Map function on every element of list:
128
129        ```python
130        def make_exciting(text: str) -> str:
131            return test.upper() + "!!!"
132
133        fun_stuff = (
134            List(["hats", "cats", "bats"])
135            >> make_exciting
136        ).unwrap()
137
138        fun_stuff == ["HATS!!!", "CATS!!!", "BATS!!!"]
139        """
140        return List([func(x) for x in self.value])
141
142    def filter(self, func: Callable) -> "List":
143        """
144        Filter list to only elements with true return:
145
146        ```python
147        x = (
148            List([1, 2, 3, 4])
149            .filter(lambda x: x % 2 == 0)
150        ).unwrap()
151
152        x == [2, 4]
153        """
154        return List([i for i in self.value if func(i)])
155
156    def unwrap(self) -> ListType[T]:
157        """
158        Return only the value of the monad without wrapping
159        it.
160
161        ```python
162        List([4]).unwrap() == [4]
163        ```
164        """
165        return self.value

Monad to apply function to all items in list.

Write functions as if they act on a single value.

Example: ```python x = ( List([1, 3, 7])

(lambda x: x + 1) (lambda x: x / 2) )

x will evaluate to List([1, 2, 4])

def filter(self, func: Callable) -> List:
142    def filter(self, func: Callable) -> "List":
143        """
144        Filter list to only elements with true return:
145
146        ```python
147        x = (
148            List([1, 2, 3, 4])
149            .filter(lambda x: x % 2 == 0)
150        ).unwrap()
151
152        x == [2, 4]
153        """
154        return List([i for i in self.value if func(i)])

Filter list to only elements with true return:

```python x = ( List([1, 2, 3, 4]) .filter(lambda x: x % 2 == 0) ).unwrap()

x == [2, 4]

Inherited Members
Monad
Monad
value
bind
unwrap
class Result(Monad, typing.Generic[~T]):
168class Result(Monad, Generic[T]):
169    """
170    Monad to handle errors. Handle exceptions on unwrap:
171
172    ```python
173    x = (
174        Result(3)
175        >> (lamba x / 0)
176    )
177
178    x.value == None
179    isinstance(x.exception, ZeroDivisionError)
180    x.unwrap_or(4) == 4
181    ```
182    """
183    def __init__(self, value: T, exception: Optional[Type[Exception]] = None):
184        """
185        Create Result monad with value and exception
186        (one of which will always be None)
187        """
188        self.value: T = value
189        self.exception = exception
190
191    def bind(self, func):
192        """
193        Execute function on value, if error is raised,
194        returned Monad will have value of "None" and an exception.
195
196        Otherwise, exception will be None, and value will be return.
197
198        If exception already exists, function won't be executed.
199        """
200        if self.exception:
201            return self
202        try:
203            return Result(func(self.value))
204        except Exception as exception:
205            return Result(None, exception)
206
207    def unwrap(self) -> T:
208        """
209        If exception is raised, will raise exception.
210        Otherwise will evaluate to value.
211        """
212        if self.exception:
213            raise self.exception
214        return self.value
215
216    def unwrap_or(self, value: U) -> Union[T, U]:
217        """
218        If exception is raised, will default to given value.
219        """
220        if self.exception:
221            return value
222        return self.value
223
224    def __str__(self) -> str:
225        """
226        Custom string representation
227        """
228        return f"{self.__class__.__name__}({self.exception or self.value})"

Monad to handle errors. Handle exceptions on unwrap:

x = (
    Result(3)
    >> (lamba x / 0)
)

x.value == None
isinstance(x.exception, ZeroDivisionError)
x.unwrap_or(4) == 4
exception
def unwrap_or(self, value: ~U) -> Union[~T, ~U]:
216    def unwrap_or(self, value: U) -> Union[T, U]:
217        """
218        If exception is raised, will default to given value.
219        """
220        if self.exception:
221            return value
222        return self.value

If exception is raised, will default to given value.

Inherited Members
Monad
Monad
value
bind
unwrap