
Is your JavaScript function actually pure? - kiyanwang
http://staltz.com/is-your-javascript-function-actually-pure.html
======
automatwon
If we want to be pedantic, there is no such thing as a pure JavaScript
function. If the stack is full, the invocation of ANY function will overflow
the stack. That's a side effect to the outside world. Pure functions exist
under Plato's Theory of the Form conception of the world, but not on a fixed
and finite physical computer. [http://www.johndcook.com/blog/2010/05/18/pure-
functions-have...](http://www.johndcook.com/blog/2010/05/18/pure-functions-
have-side-effects/)

If we don't want to pedantic, then a fraction of the 74% of who took the
function as 'pure' probably took this to mean being equivalent to referential
transparency. That's good enough, and I believe the author would agree.

 _I hope we can start shifting our discussions from 'is this pure?' to 'Does
this function behave like a Mathematical Function in all the situations I will
encounter in my code?'_

It's a good exercise to think about assumptions, but we shouldn't be _so_
paranoid as to check if native JS methods, namely Array.isArray, have been
overridden (unless security is a concern). When we speak about the Fibonacci
sequence, it's nonsensical to talk about f("pie"). The domain being integers
is implied in the semantics of the function. In the veins of "keep an open
mind but not so open that your brain falls out", I propose "keep writing code
with pure functions, but not so pure that your code becomes cluttered."

~~~
wldcordeiro
I ran into this article a little while ago and I had a similar sentiment. I
get the point staltz is making but it felt pedantic and heavy handed, hell you
can ignore JavaScript entirely and the same is true for just about every
programming language. Seems that there is a trend towards ideology in the
JavaScript community lately that bothers me. People obsess over pure functions
and FRP and talk so highly about these things like mutation and non-FRP are
the worst things on the planet when it comes to code.

~~~
oldmanjay
The trouble with trying to apply words like "community" to millions upon
millions of people with basically nothing but a surface connection (like they
all have used javascript, for instance) is explained nicely in Vonnegut's
"Cat's Cradle"

~~~
philh
"The JavaScript community" need not include everyone who has used javascript.
Why do you assume GP was intending it to do so?

~~~
oldmanjay
I made only one assumption - that "the JavaScript community" is a granfalloon.
Any further assumptions you assume I made are not valid.

------
chriswarbo
Reposted from
[https://news.ycombinator.com/item?id=12530652](https://news.ycombinator.com/item?id=12530652)

I disagree with some of the claims, as I think these sorts of edge-cases
mostly serve to highlight how imprecise the given pure/impure distinction is,
and how we can't always take a semantic concept from one language (e.g.
Haskell) and apply it in another (JS).

For example, if you always get "TypeError" when calling "sum()", you can
replace "sum()" with its result ("throw TypeError") and get the same
behaviour; it doesn't break referential transparency, so is it "pure"? What do
we even mean by "result" in a language with exception handling? Do thrown
exceptions count, or only return values?

Similarly, using "valueOf" doesn't show that the sum function is impure, it
shows that the sum function is higher-order. If we mapped 'Math.random' over
an array, does that make 'map' impure? What does it mean to be pure/impure
when any variable can be replaced by a 'valueOf' function/procedure?

If two expressions call the same procedures, the same number of times, in the
same order, and return the same pure function of their outputs, are those two
expressions the same? Would replacing one with the other maintain referential
transparency, and does that imply "purity"?

I think the answer is that such examples need more fine-grained notions than
just "pure"/"impure". Otherwise it's just arguing over the semantics of
English, rather than semantics of programs.

~~~
catnaroek
Rather than talking in terms of “purity”, it's better to talk in terms of
“effects”. Raising an exception is an effect. Returning different results when
passed the same arguments twice is an effect. I/O is an effect. Looping
forever (also known as “divergence” in some circles) is an effect. What you
call “purity” is just the absence of effects.

From the point of view of programming language semantics, what all effects
have in common is that they reduce the extent to which equational reasoning is
applicable to our programs. For example,

    
    
        f(x) == f(x)
    

doesn't always hold if `f` is a non-deterministic procedure, and

    
    
        function foo_bar() {
            var x = foo();
            var y = bar();
            return (x, y);
        }
    

isn't the same as

    
    
        function bar_foo() {
            var y = bar();
            var x = foo();
            return (x, y);
        }
    

if `foo` and `bar` have effects that don't commute.

\---

Higher-order functions also give us an interesting possibility: effect
polymorphism. Let's consider the humble `map` function, operating on lists:

    
    
        map _ [] = []
        map f (x :: xs) = f x :: map f xs
    

Intuitively, it's clear that `map f` is an effectful procedure if and only if
`f` is an effectful procedure. This notion can be formalized by giving `map`
the following type signature:

    
    
        forall (a b : Type) (f : Effect). (a -f-> b) -> (List a -f-> List b)
    

Where `Foo -Bar-> Qux` means “procedure with argument type Foo, effect Bar,
and return type Qux”, and `Foo -> Bar` is sugar for `Foo -Pure-> Bar`.

~~~
chriswarbo
Algebraic effects are a way to make well-behaved languages (Haskell, Idris,
etc.) more intuitive/generic/terse/etc. to write; I'm not sure how useful they
are for reasoning about an arbitrary snippet of JS taken from the wild.

One problem stated in the article is we can't even rely on things like
variable substitution to be effect-free, due to mechanisms like "valueOf". For
example, in your "map" definition, the value of "f" called at the head of the
list may differ from the value of "f" passed to the recursive call.

With this level of monkey-patching possible, it becomes quite hard to say
anything about a piece of code other than "it's return type is Any, it might
throw an exception, and it could perform arbitrary effects" :(

~~~
catnaroek
> I'm not sure how useful they are for reasoning about an arbitrary snippet of
> JS taken from the wild.

Probably not much, indeed.

> One problem stated in the article is we can't even rely on things like
> variable substitution to be effect-free

I have no idea how to make sense of this. The presence or absence of effects
is an object language notion. Variable substitution is an operation on the
syntax of this object language, and syntax necessarily lives in a metalanguage
(e.g., the language in which a compiler or interpreter is written).

EDIT: Forgot about explicit substitutions, which make substitution an explicit
reduction step in the object language. Even then, I don't see how substitution
can be effectful by itself.

> For example, in your "map" definition, the value of "f" called at the head
> of the list may differ from the value of "f" passed to the recursive call.

At least in a call-by-value language, this is should never the case. Funny
things can happen if lexical environments are first-class objects, of course.

~~~
chriswarbo
> I have no idea how to make sense of this.

Maybe I should have used a different phrase. I meant that an expression
consisting of a variable (e.g. "x") may be evaluated as if it were a
dynamically-dispatched function call (e.g. "x.valueOf()"), so even the most
innocent-looking expression may contain hooks to which someone may have
attached an effect.

Even though JS is call-by-value, implicitly-called methods like "valueOf"
allow values in normal form to act like thunks with arbitrary side-effects,
which causes a dependence on evaluation order even for such innocent-looking
expressions as "x".

~~~
catnaroek
Wow, this is terrible. I already knew I didn't know JavaScript. But I didn't
know the extent to which I don't know JavaScript.

------
mthq
The author uses the definition that a pure function must return identical
outputs on identical inputs. I actually think a better definition is that the
output of a function cannot depend on anything but it's inputs. If we use that
definition then the first example he gives where the `sum` function violates
pureness is actually not a violation. This is also justified by interpreting a
`.valueOf = Math.random` field as a random variable, making the procedure
`sum` a map from a random variable to a random variable.

~~~
keeperofdakeys
The definition of a pure function is pretty well defined
[https://en.wikipedia.org/wiki/Pure_function](https://en.wikipedia.org/wiki/Pure_function).
Math.random depends on hidden state, so (generally) any function using it
isn't pure. This doesn't matter much in a language like Javascript, but other
languages depend upon this definition (like the lazy function evaluation in
Haskell).

~~~
DougBTX
Is that definition of pure closer to mthq's definition or the one in the
article? I'm not sure.

It says "same argument value(s)", but how does that apply when the argument is
a function? The article seems to be saying that if the same argument is passed
in every time, then the function is only pure if it returns the same value
every time, regardless of whether the argument is a pure function or not.

~~~
asdfaoeu
A pure function can't call a non pure function. So you can pass in a non pure
function so long as you don't call it.

------
svalorzen
The assumption in this article is that as long as a line of code as the same
characters, its output will be the same. But this makes no sense. Consider:

    
    
        var arr = [0,1,2]
        sum(arr);
        arr = [1,2,3];
        sum(arr);
    

The two sums will have different results. What the article does is in essence
calling

    
    
        sum([Math.Random(), Math.Random(), Math.Random()]);
        sum([Math.Random(), Math.Random(), Math.Random()]);
    

I don't see why one would expect the output of such a call to be always the
same, if you are in all effect changing the input. Even if the characters you
typed are the same, if the values that are passed around change the result of
a function call will change. "Pure" is a property with regards to the values
passed to a function, not to the characters typed to call it.

~~~
philplckthun
I believe as well that the article is imprecise, since it elaborates on a
trick question and tries to just inform about the term "pure" being hard to
define, especially in a dynamic language.

To be referencing an exact quote of the article to make it very clear:

    
    
      var arr = [{}, {}, {}];
      arr[0].valueOf = arr[1].valueOf = arr[2].valueOf = Math.random;
    

Obviously this relies on multiple function calls, so the output of the
original function `sum` is still predictable. I think predictability and the
absence of side effects is still a much better definition for pure functions.

It's also a necessary definition in JS. Saying that 'the same call of the
function with similar enough arguments will always yield the same output' is
not a sufficient definition and way too simplified.

------
dclowd9901
Dear god this article is annoying. I could spend an hour nitpicking the bs-ey
wonk that André is spewing here, but that would be pointless, so I'd like to
make a more macro point: Can we please get away from pedantism and trivial
analysis as a display of intelligence?

------
bbcbasic
Sorry... in Haskell there is performUnsafeIO. Nothing in Haskell is therefore
guaranteed to be pure!

Hello Downvoter: I know it is silly but the article is talking about equally
silly JS by overriding valueOf.

~~~
masklinn
The question is asked providing the entire implementation of the respective
sum functions in terms of primitives. There's no room for unsafePerformIO in
the Haskell code (assuming prelude's `(+)` and `foldl`).

There _could_ be if the Haskell version was typed on `Num a`, but it's typed
on `Int`, which is a standard type with a known pure Num instance.

~~~
bbcbasic
Ok sure but I was meaning in general.

Anyway pass in (repeat 0) and you get a side effect. :-)

~~~
tel
Haskell is well-known not to be pure in supporting non-terminating
computation.

------
ajuc

        sum(); // TypeError: Cannot read property 'length' of undefined
    
    

That doesn't mean it's not pure.

    
    
        var arr = [{}, {}, {}];
        arr[0].valueOf = arr[1].valueOf = arr[2].valueOf = Math.random;
        sum(arr); // 2.393660612848899
        sum(arr); // 2.3418339292845998
        sum(arr); // 2.15048094452324
    

Oh come on. By that logic there are no pure functions in C, because you can
play with #define. Definitions are meant to be useful.

------
pwdisswordfish
If we're being nitpicky about type checking...

    
    
        function f() { return Math.random() < 0.5; }
        f.toString = function () { return 'function toArray() { [native code] }' }
        Array.isArray = f;
    

What, use Function.prototype.toString instead? What if someone tampered with
that too?

------
badthingfactory
Don't pure functions come with the disclaimer that no one does anything stupid
like overriding the Date constructor or valueOf?

Otherwise, we have to either come up with a different name for pure functions
with a modified meaning, or write a bunch of absolutely ridiculous defensive
code in every single function we want to truly be pure.

------
kazinator
Throwing an error doesn't make a function impure. If the calculation is
erroneous then the expression sum() _has no value which could replace it_.
I.e. the definition of pure being that we can replace sum(a) by its value
without changing the behavior of the program, simply doesn't apply. The
exception is effectively equivalent to the situation of there not being a
value.

As for this:

    
    
      var a = {}; a.valueOf = Math.random;
      var fn = x => x * 10;
      fn(a); // 5.107926817373938
      fn(a); // 3.4100775757245416
      fn(a); // 5.1903831613695095
      // Same input, different outputs!
    

Calling that "same input" just because it has the same syntax "a" is just ...
plain silly. It's obviously "a" that is impure not the "fn" multiplication
function. "a" is behaving like a macro which denotes the retrieval of a
pseudo-random number.

It's simply not reasonable extend the definition of a functions' purity to
include *the behavior of the expression(s) which are evaluated to provide its
arguments.

(I would say that it's still not reasonable even under non-strict evaluation.)

------
yokohummer7

      > Well, in an actual functional programming language like PureScript or Haskell we can:
      >
      > sum :: [Int] -> Int
      > sum = foldl (+) 0
      >
      > The list cannot be undefined, cannot be null.
    

Yes it can?

    
    
      main = putStr "The result is: " >> print (sum undefined)
      
      The result is: 
      Program error: Prelude.undefined
    

So by the same logic Haskell doesn't have pure functions, either?

------
fmap
Defining what a pure function in JavaScript _is_ seems to be the difficult
problem here.

Maybe we should call a function pure if "given identical, _pure_ arguments, it
returns identical, _pure_ results". You would also need to define when an
object is pure. That's already difficult, as the semantics of JavaScript
objects are so weird. It's quite likely that we would have to replace
"identical" by some weaker notion of relatedness which is merely preserved by
"pure" functions...

Anyway, this might be useful as a static analysis for a JavaScript vm. It
depends on how much wild JavaScript code is actually "pure" in any reasonable
sense of the word. Let's just sum this up and say that JavaScript is really
not a nice language to analyze.

------
rictic
Along the same lines as worrying about someone redefining `Array.isArray`,
someone could compile your Haskell with its own prelude, including their own
definition for `Int`.

There's a point at which the pursuit of purity becomes the pursuit of
sanctity, in the sense of Haidt's moral foundations
([https://en.wikipedia.org/wiki/Moral_foundations_theory](https://en.wikipedia.org/wiki/Moral_foundations_theory)).
It's not about a rational tradeoff at that point, it's about easing an anxiety
and sense of uncleanliness. I say this because I'm prone to doing it too, and
I've noticed that I can spend a lot more time and energy than is worthwhile
"cleaning" code.

------
philplckthun
Obviously due to JavaScript being a scripting language and dynamically typed,
we have to make certain assumptions so that we can even consider calling any
function "pure".

It's nice that André wants to warn us here about the fact that the term "pure"
is more or less accurate depending on the language.

In JS it can depend heavily on previous assumptions and the context, but I'd
argue that this is often only due to the arguments that are being passed to a
function. A non-safe function that isn't type will always be impure in some
contexts. But for sanity reasons we must be able to make assumptions about the
arguments.

------
cousin_it
Good article. Yes, the idea of purity becomes less useful in OO languages.
That's not specific to JavaScript or even to dynamic typing. Try summing the
elements of a Collection<Integer> in Java and your code will also be impure,
because Collection is an interface whose implementation can do anything. I
agree that JS goes too far in allowing you to impersonate numbers, but most
real-world code uses interfaces in some way, so you can't make it pure without
changing your language into Haskell.

------
sebringj
I'm too stupid to see how this is either practical or relevant in a loosely
typed language. The example was making assumptions that it is an array with
integers. It was also making an assumption a hacker is not hijacking your code
to put strange values into your array etc. This reminds of John Resig's super
complicated tricky JavaScript examples only a very small number of people
would even care to try to understand but slightly less purposeful. Probably
good for that type of crowd.

------
phyzome
I bet you can still break that last code block by having an isArray accessor
that returns the "proper" function the first time and a broken one the second
time. :-)

------
agrafix
If you compare Haskell with JavaScript, all JavaScript functions would run in
the `IO` monad because you can always (even implicitly as in the article) run
an impure action even if everything else seems pure. In Haskell, if your
function is not monadic, you can't "run" the monadic action. On the other
hand, you don't see exceptions in Haskell's type signatures and you can throw
them in pure code. Hm! ;-)

------
chukye
I would also use some `demand` function or argument-validator in JavaScript to
validate that, thats a good practice in anyway, like: `function sum(arr) {
goodArrayOfNumbers(arr); ... }`.

I think that; in JavaScript you NEED to validate your arguments with something
like that, you really cant assume nothing. ("Dont assume it, prove it!").

------
nicolast
Trivia: assuming the input to `sum` below is guaranteed to always be a non-
trickery array of numbers, so it always returns the same result for the same
input, is the function 'pure'?

    
    
        var x = 0;
    
        function sum(arr) {
            x += 1;
    
            return arr.reduce(function(a, b) { return a + b; }, 0);
        }

~~~
eyelidlessness
I don't understand how this is ambiguous. `sum` is changing state outside its
scope (a side-effect). There is no definition of "pure" I'm aware of that
would include this implementation.

------
thght
I would use my dynamic type checker/enforcer to make it pure:

    
    
      var types= require( 'types.js' );
    
      function sum(arr) {
    
        arr = types.forceArray( arr );
        var z = 0;
        for (var i = 0; i < arr.length; i++) {
          z += types.forceNumber( arr[i], 0 );
        }
        return z;
      }

------
wybiral
If you're using valueOf like that then you already did something wrong.
Reevaluate your life choices.

------
catnaroek
Is this function pure?

    
    
        function make_pair(x, y) {
            return { former: x, latter: y };
        }

------
pmarreck
Given the emerging importance of separating "pure" code from "side-effecting"
code, what frontend JS techs actually enforce this division, as opposed to
just leaving it up to the fastidious developer? Elm? Haskell/Haste?

------
ohitsdom
For those of you that write in JS (and not TypeScript), do you use "void 0"
instead of "undefined"? I use undefined and would hate to switch to void 0,
since it's less clear and unnecessary thanks to tools like JSHint.

~~~
pygy_
`undefined` is a variable that can (could? It may have changed with strict
mode or ES6) be set globally to arbitrary values.

You can use `undefined` safely with an IIFE wrapper:

    
    
        (function(undefined) {
            // you're safe here
        }());

------
lr4444lr
Premise is interesting, but by the end, JS fails the author's standard of
purity not really because of its function implementation (or any variant
thereof) but because of its dynamic-weak type system.

------
chadscira
I mean what happens when someone does

    
    
        Array.isArray = () => true;
        Array.isArray.toString  = () => 'function isArray() { [native code] }';
    

:/

------
newshorts
Should have been titled:

"Is Your JavaScript Function Actually Pure? An impractical guide to Pedantic
JavaScript."

------
dustingetz
No

    
    
        (map println [1 2 3])
    

Map is RT even when println is not

------
caub
so var sum=(...a) => a.reduce((x,y)=>x+y) is pure?

