
Side Effects vs. Promises - dangoor
http://www.blueskyonmars.com/2015/10/01/side-effects-vs-promises/
======
inglor
So much wrong in one place. A promise _is_ the IO monad, with `then` being
`>>=` [1].

If you write your promises code not to cause explicit side effects - it won't
and you get pure code.

The fact you have (controlled!) side effects is the whole purpose of the IO
monad. You can't avoid side effects or your program wouldn't do anything, a
promise keeps the side effects in a box (the promise) and lets you code using
boxes so that your code is purer. Of course, not all your code can be
_actually_ pure because all code needs side effects.

The FUD about async/await is even stranger. JS _has_ async/await, it already
shipped in Microsoft Edge (IE) and it's coming to other browsers. It is based
on promises (just like it's based on Tasks = Promises) in C#.

The fact people talk so much about this without learning the topic is amazing.
I'm genuinely disappointed with the quality of discussion in HN over these
topics lately.

Monet is also a bad implementation of the IO monad in JS, in fact it's not an
implementation at all. Here is an issue I opened there a while ago:
[https://github.com/cwmyers/monet.js/issues/25](https://github.com/cwmyers/monet.js/issues/25)

[1] up to the fact `.then` does both `fmap` and `>>=` through overloading
based on the return type. Seriously, if you remove the exceptions and the fact
you can return an unwrapped value it's `Promise a -> a -> Promise b -> Promise
b` for then's signature (the Promise a being `this`, the a -> Promise b` being
the handler and the return value being itself).

~~~
dangoor
> If you write your promises code not to cause explicit side effects - it
> won't and you get pure code.

The way I've seen promises used in JS is always in this pseudocode form:

\- initiate something side effecty (making an HTTP request, for example)

\- resolve the promise once the value is ready

Is there something you have in mind for how we'd write promises-based code
without side effects in JS?

~~~
inglor
The same way you'd write "code that doesn't cause side effects" in Haskell.
You cause side effects. The thing is with promises (like IO) the side effects
are limited to a box you can open, process and close.

So given the same input the same function will always return the same output.
If you have a function that asynchronously computes a random number - it won't
return different numbers and will always return a promise for a random number.
It will never be observable that being called with a different input it
produced a different output (as long as you don't mutate global state or
anything like that).

In Haskell, IO is implemented in a way that causes side effects at the
platform level. If there are no side effects there is nothing going on and the
program is a no op after all :) Monads like IO are about _limiting_ side
effects and _controlling_ state.

In Haskell, the lack of side effects is by the fact IO is provided by the
platform. There is no fundamental difference between Haskell's `getLine` and
the DOM's `fetch`, both return a boxed value.

~~~
dangoor
Thanks for the followup!

> In Haskell, the lack of side effects is by the fact IO is provided by the
> platform. There is no fundamental difference between Haskell's `getLine` and
> the DOM's `fetch`, both return a boxed value.

In his talk about "effects"[1], Chris Armstrong also talked about making
testing easier in Haskell, though I paid less attention to that part, to be
honest.

`fetch` returns a promise, but calling fetch causes the side effect to occur
immediately. I don't know if that's how `getLine` works in Haskell. If I want
to test code that calls `fetch` without an HTTP request actually hitting the
network, I need to put in a mock `fetch`.[2]

 _Or_ , if there was another return type that put the side effect in a box
that the platform handles later (or not at all, in the case of a test), that
seems like a win.

[1]:
[https://www.youtube.com/watch?v=D37dc9EoFus](https://www.youtube.com/watch?v=D37dc9EoFus)

[2]: another option, pointed out on Twitter, is that a promise (either from
the fetch or from the test) could be passed in to the code under test. That
seems less pleasant but requires no new machinery.

~~~
inglor
Fetch doesn't actually make an HTTP call - that's the thing :) Fetch boxes a
value so that the _platform_ can make an HTTP call.

You couldn't implement `fetch` yourself without platform APIs and you
certainly couldn't do it in JavaScript without the DOM. The second suggestion
you pointed (from Twitter) is exactly what you'd do in Haskell (you'd pass an
IO from somewhere else). You can call it "pass an effect" if you'd like but
it's the same thing :)

~~~
dangoor
Indeed, the platform is responsible for the HTTP call, so I see what you mean.
The side effect is happening in the platform itself. I should watch the second
part of Chris Armstrong's video to see how he approached making testing nicer
in Haskell.

------
saosebastiao
All of the back and forth on async callbacks vs async/await vs promises makes
me sad that more people haven't yet discovered async computation expressions
as found in F#. It is easily the most intuitive and expressive form of
declarative async computation out there. I wish F# were more popular so it's
revolutionary concepts like this one would bleed their way into other
languages.

~~~
Chattered
I haven't used F# much, but aren't computation-expressions just a Haskell like
do-notation, which you get for free because the async type constructors are a
monad? Ocaml and Haskell should have this, at least, with lwt and its syntax
extension, and Async respectively. I'm not sure what the situation is in
Scala, but they surely have something similar with their generic for-
comprehensions.

Outside of those, I don't know what the story is.

~~~
edgyswingset
I'm not a Haskeller and haven't played with monads _knowing_ they were monads,
but async in F# basically works like this:

1\. Call the function with async { } block and have it return a computation
object

2\. Start that computation object in three primary ways:

    
    
      a. On another thread, then wait (non-blocking) on it to finish
    
      b. On another thread but don't wait for it to finish
    
      c. On the current thread and don't wait for it to finish
    

There's a lot more details but that's going to cover probably 95% of the use
cases.

My takeaways from it are:

a. Super easy to write code for it that's easy to read

b. Really hard to fuck it up

In my opinion, it's superior to the model that C#/VB have ... and pretty much
every other language I've tried async support on. Have yet to use Haskell for
it.

------
msoad
I never liked the event oriented async programs. They are just harder to
debug. Once you have more that a handful of evented object with couple of
possible events you pretty much guaranteed to not know where to look for bugs.

Promises are not perfect either, but at least you can inspect them and thanks
to new developer tools you can do it much easier.

~~~
munro
Bluebird makes debugging easy with long stack traces [1], and unhandled
rejection detection [2]. Without the unhandled rejection detection, it's easy
to get burned by forgetting to handle a rejection case, or use `.done()`.

[1]
[https://github.com/petkaantonov/bluebird/blob/master/API.md#...](https://github.com/petkaantonov/bluebird/blob/master/API.md#promiselongstacktraces
---void) [2]
[https://github.com/petkaantonov/bluebird/blob/master/API.md#...](https://github.com/petkaantonov/bluebird/blob/master/API.md#promiseonpossiblyunhandledrejectionfunction-
handler---undefined)

~~~
inglor
As a bluebird contributor it's worth pointing out that while our stack traces
are pretty sweet - unhandled rejection tracking exists in most modern promise
libraries.

------
dclowd9901
So what are we talking about here? Just wrapping side effects with a
constructor so they're isolatable? You could just modify the Function
prototype to mock a constant return for the same purpose, and it'd be
completely transparent (no API littering the entire codebase).

------
k__
I use promises most of the time. In frameworks like koa they even got a bit
nicer.

Is there something like this for event systems?

I mean, if I send stuff to the server and wait for it to answer, promises work
really well.

But what about things like onClick? The promise can only resolve once.

~~~
inglor
For this there are Observables and async iterators. I warmly recommend
[https://channel9.msdn.com/Events/Lang-NEXT/Lang-
NEXT-2014/Ke...](https://channel9.msdn.com/Events/Lang-NEXT/Lang-
NEXT-2014/Keynote-Duality) .

I've also personally been using Rx and RxJS a lot and have been enjoying it a
lot over the past few years. Observables and async iterators are both features
that are on standards track for the language. See
[https://github.com/zenparsing/es-
observable](https://github.com/zenparsing/es-observable) and
[https://github.com/Reactive-Extensions/RxJS](https://github.com/Reactive-
Extensions/RxJS)

------
greggman
Since we're talking about promises ... can anyone point to some good patterns
for nested promises and not losing errors to the either?

Every time I use promises I find that I lose errors and it takes hours of
adding console.logs all over the place until I find the place with the error
some nested promise is hiding.

~~~
inglor
Hey! In NodeJS we added a handler that helps you with that here:
[https://nodejs.org/api/process.html#process_event_unhandledr...](https://nodejs.org/api/process.html#process_event_unhandledrejection)

Here is a talk I gave about adding it last June:
[https://www.youtube.com/watch?v=LGpmUyFnyuQ](https://www.youtube.com/watch?v=LGpmUyFnyuQ)

Personally, I recommend you use a faster richer userland library like Bluebird
which has stronger unhandled rejection tracking.

------
wildpeaks
I can't wait for async/await native in Node because they're imho required for
really unlocking its potential; sticking only to es6 promises and generators
feels incomplete.

Edit: I mean ES7 async, not async.js (even though it's a great library)

------
arianvanp
I've recently decided that all this async and evented stuff is just too hard
to reason about. A proper language should have (lightweight) threads if it
wants to support concurrency. Seems a lot easier to reason about.

See go or haskell for example.

~~~
inglor
You know, Haskell's IO is pretty much equivalent to JavaScript's promises.
JavaScript's generators let you have do notation in JavaScript, and using
async/await the "evented" stuff becomes not so evented and pretty easy to
reason about.

I agree that it's not optimal, but the alternative (implicit IO) has its own
share of pitfalls either. The async code that gets produced with modern
JavaScript or Python is pretty easy to follow.

------
berryg
Take a look at elm-lang.org. It is a functional language that compiles to
javascript. Effects are implemented as Tasks (as I understand kind of like a
Monad). It has very good interoperability with javascript code.

~~~
dangoor
Yeah, Richard Feldman's Strange Loop talk[1] touched upon Tasks and they do
indeed look exactly like what I'm looking for.

Elm does look very nice. Not sure if I'd get the team on board with that kind
of change ;)

[1]:
[https://www.youtube.com/watch?v=FV0DXNB94NE](https://www.youtube.com/watch?v=FV0DXNB94NE)

------
krisdol
Personally, I hate promises, but on topic

> write unit tests for your code without mocking, by specifying the expected
> content, results, and order of side-effects performed by a function

No mocking? This is absurd. Some functions rightfully hit databases and the
network. It either gets tested the right way, with mocks; the wrong way, with
actual network calls; or not at all, as the author seems to suggest. Mocking
belongs in some part in unit tests

~~~
bonobo3000
If you can write very pure code, mocking is not needed. For example, a
function for a database call could return a closure of what to do given a
cursor into the database, or a SQL query which can be tested. The side-
effecting functions themselves would just create a connection to a db or
network host and do exactly what is prescribed in the request, making them
simple enough to not need to unit test.

This is one of the big benefits of FP and pure functions really. I'm sure
there would be practicalities that get in the way of doing this cleanly for
all code, but it can go a surprisingly long way. The idea is to write a
completely pure core and restrict input/output to be through a few small and
obviously correct functions.

~~~
lkrubner
"return a closure of what to do given a cursor into the database"

Some people would insist that the closure is a mock, but otherwise this is
correct, using closures in this way does help isolate one's tests.

------
ihsw
Promises in the JS world have lost out to async[1].

The callback pyramid madness has been traded for a simple and flat
waterfall[2].

The constant .then and .catch when working with promises naturally makes a lot
of people uncomfortable, and on top of that the async library provides a
plethora of other collection manipulation and control flow utilities.

Frankly I think managing side effects should be abstracted away, and
furthermore I cannot wait for C#-like async/await to become first-class
citizens in the JS world.

[1] [https://www.npmjs.com/package/async](https://www.npmjs.com/package/async)

[2]
[https://www.npmjs.com/package/async#waterfall](https://www.npmjs.com/package/async#waterfall)

~~~
aikah
> Promises in the JS world have lost out to async[1].

What (js) planet are you living on ? async package certainly has it's place,
but the fact that promises are object you can pass around make them more
useful than any async package.

> Frankly I think managing side effects should be abstracted away, and
> furthermore I cannot wait for C#-like async/await to become first-class
> citizens in the JS world.

Well too bad, because async/await will work with promises.

And I quote the blogpost :

> In practice, returning side effects rather than performing them and
> returning promises can increase the size of the “functional core” of your
> application, which is a win in my book.

Which is exactly what I'm saying.

~~~
notNow

      Well too bad, because async/await will work with promises.
    

and promises are built on callbacks at the lower level.

I just don't really get these callback-intolerant people's averse reaction
towards this construct when it's literally all over the place and those new
abstractions are there to conceal them which is a good idea by the way but not
to extinguish them completely as they might hope or believe.

