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
194 stars 21 forks source link

issue in the IO monad implementation #8

Closed PyAntony closed 3 years ago

PyAntony commented 3 years ago

Hi,

I believe there might be a problem with the IO monad. If you call run() it actually returns the function passed, it doesn't execute it. For example:

# you have to use the lambda syntax, If not print() will execute eagerly
io = IO(lambda: print('hello world!'))

io.run()
# returns the lambda function: <function __main__.<lambda>()>

# to actually run the function:
io.run()()
# prints 'hello world'

In order for functions like map() or bind() to work properly run() should be implemented as return self.value()(), not just return self.value().

Either this is the case or I am totally misunderstanding this IO monad usage.

jasondelaat commented 3 years ago

This example works as expected (that is, prints 'hello world!' to the console) for me. I'm not sure why it's not doing that for you.

Can you try copy/pasting this example and tell me if it works?

from pymonad.io import *

def io_print(x):
    return IO(lambda: print(x))

io = (IO.insert(1)
      .map(lambda x: x + 20)
      .bind(io_print)
)

io.run() # prints 21

I'm not sure what's going wrong for you. Maybe show me your import statements for your example too, just in case. What version of python are you using? It's a weird problem but I'll do my best to figure out what's wrong.

jasondelaat commented 3 years ago

I also used 'run' for State. I'm wondering if there might be some name collision happening and the wrong method getting called somehow. Not sure how that would happen but I'll check. Might not have time until tomorrow afternoon to really dig into it though.

Are you also importing any of the state monad stuff in the same file where you're running this example?

PyAntony commented 3 years ago

found the issue! not sure if this is meant to work this way though. See the difference in imports and the IO function implementations:

from pymonad.io import IO

def IO(function: Callable[[], A]) -> _IO[A]:
    return _IO(function, None)

io = IO(lambda: 1)
print( io.run() ) # prints 1

# ----------------------
from pymonad.operators.io import IO

def IO(io_function: Callable[[], T]) -> _IO[T]:
    # notice the difference in the return statement
    return _IO(lambda: io_function, None)

io = IO(lambda: 1)
print( io.run() ) # prints <function <lambda> at 0x000001FCEA63CF70>

If the second implementation (in the operators module) is correct, then how should we import the operators module correctly?

jasondelaat commented 3 years ago

Ah. Hm. Yep, looks like that lambda probably shouldn't be there in the operators package. Should be an easy fix but I'll have to check the tests too. They pass now which makes me think I've done something wrong. Should be able to get to it tomorrow afternoon sometime.

jasondelaat commented 3 years ago

Okay, it really was just a matter of removing that lambda. Should be fixed now. I'll leave this open for a day or two just in case you're still having problems. Thanks for tracking this down, greatly appreciated.

PyAntony commented 3 years ago

thank you!!!

jasondelaat commented 3 years ago

Alright, looks like this has in fact been resolved so I'm closing it up.