suned / pfun

Functional, composable, asynchronous, type-safe Python.
https://pfun.dev/
MIT License
151 stars 14 forks source link

either usage #38

Closed galen1090 closed 4 years ago

galen1090 commented 4 years ago

In this example I just want to transform Right to Data and Left to Error

@dataclass
class Error:
    msg: str

@dataclass
class Data:
    data: str

def i_can_fail2(s: str) -> Either[str, str]:
    # do something 
    return Right(f"{s}!")

def i_can_fail1(s: str) -> Either[str, str]:
    if s == 'illegal value':
        return Left("illegal value")
    return Right('Ok')

def func1() -> Either[Error, Data]:
    # what about a map but only for Left value?
    return i_can_fail1('illegal value').and_then(i_can_fail2).map(lambda x: Data(data=x))

I can do it with few lines of code, but I want to known if there is a proper way to do this. Thanks

suned commented 4 years ago

Either is defined as a union type, so the most type friendly way of doing it would be to use isinstance. But even more instructive, why not change the type of the either to Either[Error, Data]?

def i_can_fail(s: str) -> Either[Error, Data]:
    if s == 'illegal value':
        return Left(Error('illegal value))
    return Right(Data('Ok!'))

either = i_can_fail1('illegal value')
if isinstance(either, Left):
     either.get  # type of either.get is inferred to be 'Error'
else:
     either.get  # type of either.get is inferred to be 'Data'

If you wan't to avoid the Error and Data wrappers altogether, but still be able to distinguish between the error and str type during typecheck you could use https://docs.python.org/3/library/typing.html#newtype

suned commented 4 years ago

Note that i_can_fail(...).map(lambda x: Data(x)) actually produces an Either[str, Data], which doesn't sound like what you want.

Also note that you can achieve the same thing with i_can_fail(...).map(Data) (because constructors are just callables).

galen1090 commented 4 years ago

But even more instructive, why not change the type of the either to Either[Error, Data]?

because in this use case, i_can_fail1 and i_can_fail2 are not under my control or because they are used by other functions with this signature, and for them Error and Data aren't even in scope. It's just an example to properly understand how to transform Either to my needs.

suned commented 4 years ago

because in this use case, i_can_fail1 and i_can_fail2 are not under my control

Ah, I see. In that case I would just convert from Either to Error and Data with isinstance.