getify / monio

The most powerful IO monad implementation in JS, possibly in any language!
http://monio.run
MIT License
1.05k stars 58 forks source link

Any "ap" alternative #14

Closed Djordjenp closed 2 years ago

Djordjenp commented 2 years ago

Hello Kyle,

I was looking at some Monad libraries and they usually have "ap()" method which applies function (returned from the current monad action) to the value returned by IO monad we pass as argument to "ap()". Are there any alternatives in Monio?

getify commented 2 years ago

Most of the monads in Monio are Applicatives and have the ap(..) method. If you're holding a function in a non-IO monad (like Maybe), those already have the ap(..) method you need, and you'd just pass in an IO instance and it should work I believe:

const incM = Maybe.from(v => v + 1);
const twoIO = IO.of(2);

incM.ap(twoIO);  // IO(3)

Is that what you need?

IO (and its variants) aren't Applicative themselves currently (used to but then I took it off because I didn't think it made sense).

I'm open to making IO be Applicative again, but I'd like to see a code use-case (even a demo using another lib) to determine how best to design/implement it.

Djordjenp commented 2 years ago

Thank you for your response Kyle

What I wanted is to get element from the DOM and set its text content, for that I used IO monad to wrap those side effects.

const getElement = id => IO(() => document.querySelector(id))
const setElementText = element => text => IO(() => element.textContent = text)

I figured I can do this:

getElement('h1').map(setElementText).run()('New Title').run()

But it feels kind of strange because I need to pass the argument after calling "run()" and I need to call "run()" twice, so maybe there is a better way? I know I can just swap arguments position in "setElementText" function so I can do this:

const setElementText = text => element => IO(() => element.textContent = text)
getElement('h1').chain(setElementText('New Title')).run()

Now this works, but I am interested is there some alternative to first example above when I call "run()" twice

getify commented 2 years ago

That's exactly what chain(..) is for:

getElement('h1').chain(el => setElementText(el)('New Title')).run();

As you mentioned though, changing the param order of setElementText(..) is better, if possible. It's more "canonical FP" design for multi-param functions to take fixed inputs first and unfixed inputs last. As you illustrated, that often makes it easier to take advantage of point-free style (to partially-apply/specialize the function call).

Djordjenp commented 2 years ago

Oh I understand now, thank you for your all your help