bingo-functional icon indicating copy to clipboard operation
bingo-functional copied to clipboard

Shouldn't IO::bind be lazy?

Open OlexandrPopov opened this issue 4 years ago • 3 comments

Consider the following code:

use Chemem\Bingo\Functional\Functors\Monads\IO;

$io = IO::of(function () {
    echo 'LOG 1', PHP_EOL;
})
->bind(function () {
    return IO::of(function () {
        echo 'LOG 2', PHP_EOL;
    });
});

It prints LOG 1 even $io->exec() is not called.

OlexandrPopov avatar Jun 29 '20 13:06 OlexandrPopov

Hello, Mr. Popov. IO::bind should not be lazy as it is a faculty for forward function chaining: it acts on the result of the value encapsulated inside the impure operation that exists in the monad's jurisdiction.

In your example, the function prints "LOG 1" because the function supplied to bind acts on a value of null - the return value of the unsafe operation in the monad's context.

ace411 avatar Jul 02 '20 23:07 ace411

Hello, Mr. Popov. IO::bind should not be lazy as it is a faculty for forward function chaining: it acts on the result of the value encapsulated inside the impure operation that exists in the monad's jurisdiction.

Isn't the whole point of IO is to postpone the impure operation (side effect)?

In your example, the function prints "LOG 1" because the function supplied to bind acts on a value of null - the return value of the unsafe operation in the monad's context.

I'm not sure I fully understand what you mean.

OlexandrPopov avatar Jul 03 '20 07:07 OlexandrPopov

I just started to learn and use all these functional things but the samples I've seen in js and typescript libraries are definitely lazy and I thought that they work in the same way no matter what language you use. Here is an example based on the IO implementation from the Mostly Adequate Guide book: https://jsfiddle.net/alexanderpopov/m4kq2oaw/24/ In the example both console.log will be performed only when io.unsafePerformIO() is called. If you comment the io.unsafePerformIO(); line, console.log('LOG 1') won't be executed.

Here is another example based on fp-ts library:

import { pipe } from 'fp-ts/lib/pipeable';
import { IO, chain } from 'fp-ts/lib/IO';

const log1 = (): IO<void> => () => console.log('LOG1');
const run = pipe(
  log1(),
  chain(() => () => console.log('LOG2')),
);

run();

It's different from the implementation above but it works in the same way, console.log will be executed only when run() is called.

OlexandrPopov avatar Jul 03 '20 08:07 OlexandrPopov