It's minimal, but I'm posting things.
from typing import TypeVar, Generic, Optional, Callable
T = TypeVar('T')
U = TypeVar('U')
E = TypeVar('E', bound=Exception)
class Result(Generic[T, E]):
"""Stores a result, with either a value or an error."""
def __init__(self, value: Optional[T] = None, throwable: Optional[E] = None):
if value is None and throwable is None:
raise ValueError("value and throwable cannot both be none.")
if value is not None and throwable is not None:
raise ValueError("value and throwable cannot both be not none.")
if throwable and not isinstance(throwable, Exception):
raise ValueError("throwable must be an Exception")
if value:
self.__value = value
self.__throwable = None
if throwable:
self.__value = None
self.__throwable = throwable
def get_value(self) -> T:
if self.is_error():
raise self.__throwable
return self.__value
def get_error(self) -> E:
if self.is_error():
return self.__throwable
raise Exception("Cannot retrieve error on result without error.")
def is_error(self) -> bool:
return self.__throwable is not None
def __repr__(self):
return f"""Result(value=`{"None" if self.is_error() else self.get_value()}`, error=`{"None" if not self.is_error() else self.get_error()}`)"""
def __str__(self):
if self.is_error():
return str(self.__throwable)
else:
return str(self.__value)
@staticmethod
def of(value: T) -> 'Result[T, E]':
if isinstance(value, Exception):
return Result.of_error(value)
return Result(value, None)
@staticmethod
def of_error(error: E) -> 'Result[T, E]':
if not isinstance(error, Exception):
return Result.of(error)
return Result(None, error)
class Lazy(Generic[T, E]):
"""Chains function calls and only executes then by calling apply."""
def __init__(self, _callable: Callable[[], U]):
if _callable is None:
raise ValueError("_callable cannot be none.")
self._callable = _callable
def map(self, _new_lambda: Callable[[T], U]) -> 'Lazy[U, E]':
return Lazy(lambda: _new_lambda(self._callable()))
def apply(self) -> Result[T, E]:
try:
return Result.of(self._callable())
except Exception as ex:
return Result.of_error(ex)
def test_result_value():
result = Result.of("abc")
assert result.get_value() == "abc"
assert not result.is_error()
result = Result.of_error("abc")
assert result.get_value() == "abc"
assert not result.is_error()
def test_result_error():
result = Result.of_error(Exception("Error"))
assert result.is_error()
result = Result.of(Exception("Error"))
assert result.is_error()
def test_lazy_apply():
result = Lazy(lambda: 1).apply()
assert result.get_value() == 1
def test_lazy_map():
result = Lazy(lambda: 1)\
.map(lambda i: i+1)\
.apply()
assert result.get_value() == 2
def test_result_map_error():
result = Lazy(lambda: 1)\
.map(lambda i: i+1)\
.map(lambda i: i/0)\
.apply()
assert result.is_error()
assert isinstance(result.get_error(), ZeroDivisionError)