jasondelaat / pymonad

PyMonad implements data structures typically available in pure functional or functional first programming languages like Haskell and F#. Included are Monad and Monoid data types with several common monads included - such as Maybe and State - as well as some useful tools such as the @curry decorator for defining curried functions. PyMonad 2.x.x represents an almost complete re-write of the library with a simpler, more consistent interface as well as type annotations to help ensure correct usage.
BSD 3-Clause "New" or "Revised" License
197 stars 22 forks source link

Cannot compose Either #37

Open benner opened 6 months ago

benner commented 6 months ago

Playing with library and and got confused.

Code:

from pymonad.either import Left, Right
from pymonad.reader import Compose

def check(x):
    if x < 0:
        return Left("cannot be negative")

    return Right(x)

def add5(x):
    return Right(x + 5)

def mul3(x):
    return Right(x * 2)

right_path=check(5).then(check).then(add5).then(mul3)
print(right_path)

left_path=check(-5).then(check).then(add5).then(mul3)
print(left_path)

f=Compose(check).then(add5).then(mul3)
print(f(-5))

Firs two prints works as expected:

Right 20
Left cannot be negative

but last one gives error:

..
.. line 11, in add5
    return Right(x + 5)
TypeError: unsupported operand type(s) for +: 'Either' and 'int'

Is this expected?

vreuter commented 3 months ago

@benner IIRC it's correct / expected, as then is designed to be "smarter" in a sense (at least, more functionally flexible), while Compose is meant to do pure function composition (i.e., not unwrapping the result of a previous monadic computation before passing it to the next chained function).

At least, that's what the then function's documentation on the either page, in conjunction with the description of Compose in the reader doc suggest to me. I'm new to the library, though, so take w/ grain of salt.