It doesn't matter if it's a simple case.
But, if it overlaps a little bit, it's quite a hassle.
from functools import partial
from typing import Any, Callable, Union
from expression import Ok, Result, pipe
from expression.extra.result import catch
from typing_extensions import Concatenate, ParamSpec, TypeVar
ArgT = TypeVar("ArgT")
ParamT = ParamSpec("ParamT")
ResultT = TypeVar("ResultT")
class Currylike:
@classmethod
def map(
cls, arg: ArgT
) -> Callable[
[
Union[
Callable[Concatenate[ArgT, ParamT], ResultT],
Result[Callable[Concatenate[ArgT, ParamT], ResultT], Any],
]
],
Result[Callable[ParamT, ResultT], Any],
]:
return partial(cls.fmap, arg)
@classmethod
def bind(
cls, arg: ArgT
) -> Callable[
[
Union[
Callable[Concatenate[Result[ArgT, Any], ParamT], ResultT],
Result[Callable[Concatenate[Result[ArgT, Any], ParamT], ResultT], Any],
]
],
Result[Callable[ParamT, ResultT], Any],
]:
return partial(cls.fbind, arg)
@staticmethod
def fmap(
arg: Union[ArgT, Result[ArgT, Any]],
func: Union[
Callable[Concatenate[ArgT, ParamT], ResultT],
Result[Callable[Concatenate[ArgT, ParamT], ResultT], Any],
],
) -> Result[Callable[ParamT, ResultT], Any]:
if isinstance(arg, Result):
if isinstance(func, Result):
return func.bind(lambda f: arg.map(lambda value: partial(f, value))) # type: ignore
return arg.map(lambda value: partial(func, value)) # type: ignore
if isinstance(func, Result):
return func.map(lambda f: partial(f, arg)) # type: ignore
return Ok(partial(func, arg)) # type: ignore
@staticmethod
def fbind(
arg: Union[ArgT, Result[ArgT, Any]],
func: Union[
Callable[Concatenate[Result[ArgT, Any], ParamT], ResultT],
Result[Callable[Concatenate[Result[ArgT, Any], ParamT], ResultT], Any],
],
) -> Result[Callable[ParamT, ResultT], Any]:
if isinstance(arg, Result):
if isinstance(func, Result):
return func.map(lambda f: partial(f, arg)) # type: ignore
return Ok(partial(func, arg)) # type: ignore
if isinstance(func, Result):
return func.map(lambda f: partial(f, Ok(arg))) # type: ignore
return Ok(partial(func, Ok(arg))) # type: ignore
@staticmethod
def fcall(
func: Result[Callable[ParamT, ResultT], Any],
*args: ParamT.args,
**kwargs: ParamT.kwargs
) -> Result[ResultT, Any]:
return func.bind(lambda f: catch(exception=Exception)(f)(*args, **kwargs))
def func(a: int, b: str, c: ex.Result[int, tp.Any]):
return [a, b]
def get_func():
return Ok(func)
func_result = get_func()
value = pipe(
func_result,
Currylike.map(1),
Currylike.map("asd"),
Currylike.bind(123),
Currylike.fcall
)
I would appreciate it if you could let me know if there is a good method that you have already provided.