
How to deal with dirty side effects in your pure functional JavaScript - kiyanwang
https://jrsinclair.com/articles/2018/how-to-deal-with-dirty-side-effects-in-your-pure-functional-javascript/
======
kyleperik
I disagree with the notion that functional programs must never contain side
effects. I think we should just be reasonable and only produce side effects
when explicit, and never mix side effect code with data processing.

We shouldn't have to rely on loopholes to satisfy any hard and fast rules. A
lot of writing good code is just being reasonable and explicit.

If you call a function to launch a missile, you'd expect that it's just going
to launch a missile and do nothing else. So why are we so concerned that it
contains side effects? What other option do we have?

Logging is a bit different. For many, logging is probably a necessity to
allowing you to look back and and understand context when things go wrong. But
when you're programming functionally, I find that it's much easier to get a
grasp on things compared to procedural code, which can become spaghetti much
quicker. I also find that bugs are much less common, or otherwise much more
obvious in functional code, negating the need for logging in the first place
(for functional code).

------
lalos
This is actually a pretty good introduction to functional programming, was not
expecting it from the title. Awesome.

------
xg15
Pure functions have of course a lot of benefits - if the goal is computing a
value. However, why would you even want to compose an interactive application
(in javascript) fully out of pure functions?

E.g., suppose you have an SPA that pulls data from a number of Rest endpoints,
then does some transforms on that data, produces some HTML and eventually
updates the page DOM accordingly.

It makes sense model the "transform" and "produce HTML" steps as pure
functions, because they are effectively side-effect free calculations. I think
it might also make sense to cheat and treat the Rest calls as pure functions,
because this will let you find out quickly in which order you should do them
and which of them you can run in parallel.

But why would you want to model the eventual update step itself as a pure
function?

~~~
masklinn
> But why would you want to model the eventual update step itself as a pure
> function?

So you can more easily debug the application across incorrect state changes:
if your update function is pure, you can very easily compare the state-before
and the state-after.

This maintenance of observability is a superset of the unit-testing bit
legulere talks about, and also opens up new features e.g. undo/redo becomes
trivial, just re-set the state to whatever you need. If you modify the
application state in-place, things become much more complex.

In a way git can be treated as that, the HEAD commit is the application state,
when you "modify the state" you don't modify that commit in-place instead you
create a new commit, then move HEAD to that new commit. Only the second step
is impure. Even amending a commit is the latter, you're not actually modifying
the old HEAD, you're creating a new one from it then moving HEAD from one to
the other.

~~~
lllr_finger
In theory, this sounds great, and I am a huge proponent of functional
programming. In practice, I've never seen it intuitive or useful.

I've been at 3 companies now where "time travel" has never been used and web
app development has been more painful than it needed to be.

It feels very much like the proper (from a CS perspective) approach, but the
language and libraries just aren't there to make it feel productive or fun. Is
there any meaningful work being done to address this, or is it perhaps a
boiling frog scenario?

~~~
masklinn
> In theory, this sounds great, and I am a huge proponent of functional
> programming. In practice, I've never seen it intuitive or useful. […] Is
> there any meaningful work being done to address this, or is it perhaps a
> boiling frog scenario?

That's how Elm works and it's glorious. IIRC there are also clojurescript
systems working that way (application state is an atom and updating it is a
transactional CAS).

But of course it also helps that the language itself lends itself to this, so
you want a language which is expressions oriented and there persistent data
structures are the default (or even the only) option. You can do the same
thing in e.g. Javascript, but the language doesn't really help and it's easy
to slip.

~~~
KingMob
Yup, the most popular Clojurescript React framework, Re-frame, works this way,
and even with other frameworks, it's encouraged.

------
ulucs
I was left wondering “Isn’t what he calls Effect really similar a Haskell
Monad?” for a large part of the article. I was really glad to see my novice FP
insticts were right!

------
kyleperik

      const fOne   = fIncrement(zero);
      const fTwo   = fIncrement(one);
      const fThree = fIncrement(two);
      // And so on…
    

Seems like this implies that each number relies on the previous. A bit
confusing?

------
beders
Let's not forget that if you are not passing immutable data-structures into
your pure functions, all bets are off.

Unless you code very defensively, there's no way you can guarantee purity,
especially in async programming.

Given f(x) = y, if x is a regular JS object, you can't guarantee purity. It
works many times because of the single-threaded nature of most JS engines, but
the moment you do something async, you can't guarantee x hasn't changed.

That's one of the many reasons I like ClojureScript. It offers interesting
guarantees that let me write robust code.

------
carapace
One thing you can do is expand your universe to include what would otherwise
be "external" effects.

Instead of reading from opaque file handles use three-tuple of (hash, offset,
length). Now you have pure input.

Instead of printing to a stream, modify a git repo. Now you have pure output.

------
joeberon
Please change the font style

~~~
almostarockstar
Yeah, it was interesting for about 20 seconds and then I got to the first code
block and my brow furrowed.

------
tempodox
As long as JS lacks partial application and the ability to define infix
operators (for monadic `bind`, etc), it's always going to be ugly. Not that JS
would be a good language for that to begin with. We need a serious functional
language that compiles to WASM.

~~~
lucideer
> _We need a serious functional language that compiles to WASM._

Aren't there already a few? OCaml?

What's "serious" in this context?

~~~
TheAsprngHacker
Really? If OCaml has a compiler to Wasm, I’m not aware of it. I’m only aware
that it compiles to JS (via js_of_ocaml or Bucklescript). Could I please have
a link to the Wasm compiler if you have it?

I read somewhere that an OCaml to LLVM compiler was attempted, but people had
problems with the GC aspect. I don’t know if this claim is true, though.

~~~
sanderre
Whenever I manage to find time to hack on it:
[https://github.com/SanderSpies/ocaml/tree/wasm-
backend](https://github.com/SanderSpies/ocaml/tree/wasm-backend).

It compiles to wasm object files and links files with LLVM's LLD. My current
challenge is ensuring that the stdlib correctly translates to wasm. The C
parts are not translated yet - which means: no gc, no exceptions, no tail
calls.

