Skip to content

Commit 52c7606

Browse files
committed
Either now has two types
1 parent bae32d9 commit 52c7606

File tree

3 files changed

+68
-20
lines changed

3 files changed

+68
-20
lines changed

dry_monads/do_notation.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,19 @@ def decorator(*args, **kwargs) -> _MonadType:
2222
except UnwrapFailedError as exc:
2323
return exc.halted_monad
2424
return decorator
25+
26+
# from dry_monads.either import Success, Failure, Either
27+
28+
# @do_notation
29+
# def test() -> Success[int]:
30+
# def example(incoming: int) -> Either[int, int]:
31+
# return Failure("abc")
32+
33+
# first = example(1).unwrap()
34+
# second = example(2).unwrap()
35+
# reveal_type(first) # dry_monads/do.py:35: error: Revealed type is 'builtins.int*'
36+
# reveal_type(second) # dry_monads/do.py:36: error: Revealed type is 'builtins.int*'
37+
# return Success(first + second)
38+
39+
# reveal_type(test()) # dry_monads/do.py:39: error: Revealed type is 'dry_monads.either.Right*[builtins.int]'
40+
# print(test())

dry_monads/either.py

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,59 @@
11
# -*- coding: utf-8 -*-
22

3-
from typing import Callable, NoReturn
3+
from abc import ABCMeta, abstractmethod
4+
from typing import Callable, NoReturn, Generic, TypeVar, Any, Union, NewType
5+
from typing_extensions import Protocol
46

57
from dry_monads.primitives.exceptions import UnwrapFailedError
6-
from dry_monads.primitives.monad import Monad, NewValueType, ValueType
8+
from dry_monads.primitives.monad import NewValueType, ValueType, Monad
79

10+
ErrorType = TypeVar('ErrorType')
811

9-
class Either(Monad[ValueType]):
12+
13+
class Either(Generic[ValueType, ErrorType]):
1014
"""
1115
Represents a calculation that may either fail or succeed.
1216
1317
An alternative to using exceptions.
1418
'Either' (or its alias 'Result') is an abstract type and should not
1519
be instantiated directly. Instead use 'Right' (or its alias 'Success')
16-
and 'Left' (or its alias 'Failure')
20+
and 'Left' (or its alias 'Failure').
1721
"""
1822

23+
_inner_value: Union[ValueType, ErrorType]
24+
25+
@abstractmethod
26+
def unwrap(self) -> ValueType:
27+
"""
28+
Custom magic method to unwrap inner value from monad.
29+
30+
Should be redefined for ones that actually have values.
31+
And for ones that raise an exception for no values.
32+
"""
33+
raise NotImplementedError()
34+
1935

20-
class Left(Either[ValueType]):
36+
class Left(Either[Any, ErrorType], Monad[ErrorType]):
2137
"""
2238
Represents a calculation which has failed.
2339
2440
It should contain an error code or message.
2541
To help with readability you may alternatively use the alias 'Failure'.
2642
"""
2743

28-
def fmap(self, function) -> 'Left[ValueType]':
44+
def __init__(self, inner_value: ErrorType) -> None:
45+
"""
46+
Wraps the given value in the Container.
47+
48+
'value' is any arbitrary value of any type including functions.
49+
"""
50+
self._inner_value = inner_value
51+
52+
def fmap(self, function) -> 'Left[ErrorType]':
2953
"""Returns the 'Left' instance that was used to call the method."""
3054
return Left(self._inner_value)
3155

32-
def bind(self, function) -> 'Left[ValueType]':
56+
def bind(self, function) -> 'Left[ErrorType]':
3357
"""Returns the 'Left' instance that was used to call the method."""
3458
return Left(self._inner_value)
3559

@@ -42,13 +66,21 @@ def unwrap(self) -> NoReturn:
4266
raise UnwrapFailedError(self)
4367

4468

45-
class Right(Either[ValueType]):
69+
class Right(Either[ValueType, Any], Monad[ValueType]):
4670
"""
4771
Represents a calculation which has succeeded and contains the result.
4872
4973
To help with readability you may alternatively use the alias 'Success'.
5074
"""
5175

76+
def __init__(self, inner_value: ValueType) -> None:
77+
"""
78+
Wraps the given value in the Container.
79+
80+
'value' is any arbitrary value of any type including functions.
81+
"""
82+
self._inner_value = inner_value
83+
5284
def fmap(
5385
self,
5486
function: Callable[[ValueType], NewValueType],
@@ -65,8 +97,8 @@ def fmap(
6597

6698
def bind(
6799
self,
68-
function: Callable[[ValueType], Either[NewValueType]],
69-
) -> Either[NewValueType]:
100+
function: Callable[[ValueType], Either[NewValueType, ErrorType]],
101+
) -> Either[NewValueType, ErrorType]:
70102
"""
71103
Applies 'function' to the result of a previous calculation.
72104
@@ -89,3 +121,11 @@ def unwrap(self) -> ValueType:
89121
Result = Either
90122
Success = Right
91123
Failure = Left
124+
125+
# def function(trigger: int) -> Either[int, bool]:
126+
# if trigger > 1:
127+
# reveal_type(Success(''))
128+
# return Success('')
129+
# else:
130+
# reveal_type(Failure(''))
131+
# return Failure('-')

dry_monads/primitives/monad.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# -*- coding: utf-8 -*-
22

33
from abc import ABCMeta, abstractmethod
4-
from typing import Generic, TypeVar
4+
from typing import Any, TypeVar, Generic
55

66
ValueType = TypeVar('ValueType')
77
NewValueType = TypeVar('NewValueType')
@@ -19,15 +19,7 @@ class Monad(Generic[ValueType], metaclass=ABCMeta):
1919
2020
"""
2121

22-
_inner_value: ValueType
23-
24-
def __init__(self, inner_value: ValueType) -> None:
25-
"""
26-
Wraps the given value in the Container.
27-
28-
'value' is any arbitrary value of any type including functions.
29-
"""
30-
self._inner_value = inner_value
22+
_inner_value: Any
3123

3224
@abstractmethod
3325
def fmap(self, function): # pragma: no cover

0 commit comments

Comments
 (0)