Pure functions in EcmaScript

# Marius Gundersen (7 years ago)

Has there been any work done on pure functions in EcmaScript? The way I imagine it, there would be a way to indicate that a function should be pure (by using a symbol or a new keyword, although I understand new keywords aren't terribly popular). The pure function is not allowed to access any variable outside its own scope. Any access to a variable outside the scope of the function would result in a Reference Error, with an indication that the reference attempt was made from a pure function. This also applies to any function called from within the pure function. The entire stack of a pure function must be pure. This also means the pure function cannot access the [this] object. Only the parameters passed to the function can be used in the calculation.

The syntax could be something like this (the @ indicates that it is pure):

function [email protected](a, b){ return a+b; }

var sum = [email protected](a, b){ return a+b; }

Marius Gundersen

# Andreas Rossberg (7 years ago)

On 28 November 2012 12:50, Marius Gundersen <gundersen at gmail.com> wrote:

Has there been any work done on pure functions in EcmaScript? The way I imagine it, there would be a way to indicate that a function should be pure (by using a symbol or a new keyword, although I understand new keywords aren't terribly popular). The pure function is not allowed to access any variable outside its own scope. Any access to a variable outside the scope of the function would result in a Reference Error, with an indication that the reference attempt was made from a pure function. This also applies to any function called from within the pure function. The entire stack of a pure function must be pure. This also means the pure function cannot access the [this] object. Only the parameters passed to the function can be used in the calculation.

The syntax could be something like this (the @ indicates that it is pure):

function [email protected](a, b){ return a+b; }

var sum = [email protected](a, b){ return a+b; }

A couple of comments.

First, your definition of "pure" is not quite correct. Any function that even returns locally created state in some form (i.e., a new object), is impure.

Second, due to the extremely impure nature of JavaScript, there aren't many useful pure functions you could even write. For example, your 'sum' function is not pure, because the implicit conversions required by + can cause arbitrary side effects.

Last, short of a static type-and-effect system, I don't see how the necessary checks could be implemented without imposing significant overhead on almost every primitive operation -- because every function, and hence almost any piece of code, might potentially end up with a "pure" function in its call chain, and would need to check for that.

# David Bruant (7 years ago)

I won't say the idea is bad, but what would be the benefit of this new type of function?

From experience on this list, if a new idea cannot prove to make a major difference with what currently exists, it is not considered to be added to the ES6 spec. The major difference can be in performance, security, language extensibility, programming idioms/conveniences, etc. Do you have reasons to think pure functions as you propose them make that big of an improvement as opposed to JS as it is?

David

Le 28/11/2012 12:50, Marius Gundersen a écrit :

# Marius Gundersen (7 years ago)

On Wed, Nov 28, 2012 at 1:20 PM, Andreas Rossberg <rossberg at google.com>wrote:

On 28 November 2012 12:50, Marius Gundersen <gundersen at gmail.com> wrote:

Has there been any work done on pure functions in EcmaScript? The way I imagine it, there would be a way to indicate that a function should be pure (by using a symbol or a new keyword, although I understand new keywords aren't terribly popular). The pure function is not allowed to access any variable outside its own scope. Any access to a variable outside the scope of the function would result in a Reference Error, with an indication that the reference attempt was made from a pure function. This also applies to any function called from within the pure function. The entire stack of a pure function must be pure. This also means the pure function cannot access the [this] object. Only the parameters passed to the function can be used in the calculation.

The syntax could be something like this (the @ indicates that it is pure):

function [email protected](a, b){ return a+b; }

var sum = [email protected](a, b){ return a+b; }

A couple of comments.

First, your definition of "pure" is not quite correct. Any function that even returns locally created state in some form (i.e., a new object), is impure.

Fair enough. A better name would probably be side effect free functions.

Second, due to the extremely impure nature of JavaScript, there aren't

many useful pure functions you could even write. For example, your 'sum' function is not pure, because the implicit conversions required by + can cause arbitrary side effects.

Functions passed to the array methods map, reduce, filter, etc would be good candidates for pure/side-effect-free functions. These functions shouldn't alter any state; they should only return a new value based on the parameter they were sent.

Last, short of a static type-and-effect system, I don't see how the

necessary checks could be implemented without imposing significant overhead on almost every primitive operation -- because every function, and hence almost any piece of code, might potentially end up with a "pure" function in its call chain, and would need to check for that.

I'm not an implementer of EcmaScript, so I don't have deep knowledge of how this could be implemented. I would imagine that a subset of a true pure function, where the only restriction would be that only variables passed as arguments to the function exist in the scope, would be relatively easy to implement. Wouldn't this be a faster implementation than todays functions, which have to keep track of scope? These side-effect-free functions would only need to contain the variables passed as parameters in their scope. Accessing anything outside the scope would result in a reference error.

As you see this is obviously a subset of the pure function as it usually is defined. The function is allowed to modify any object passed to is an argument (since, as you point out, it would be difficult to protect against this), and it is allowed to return new state.

# Marius Gundersen (7 years ago)

On Wed, Nov 28, 2012 at 1:21 PM, David Bruant <bruant.d at gmail.com> wrote:

Hi Marius,

I won't say the idea is bad, but what would be the benefit of this new type of function?

From experience on this list, if a new idea cannot prove to make a major difference with what currently exists, it is not considered to be added to the ES6 spec. The major difference can be in performance, security, language extensibility, programming idioms/conveniences, etc. Do you have reasons to think pure functions as you propose them make that big of an improvement as opposed to JS as it is?

With many new functional programming possibilities (like map, reduce, filter, lambdas) there are many scenarios where the implementer should use pure (or as I renamed it in another reply, side-effect-free) functions. Library implementers are likely interested in making functions that can take lambdas as parameters, and these lambdas (in many cases) should not alter any outside state, they should only return a value. A function with this restriction could run faster and take up less memory since it only needs the values passed to it as arguments in its own scope.

Mostly I feel this would introduce better coding practises with a focus on functional programming rather than object oriented programming. Using functions with limited scope would reduce the number of variables written to the global scope, and would reduce the amount of state in an application. Seeing as FP is a bit of a trend today (due, in part, to the popularity of JavaScript), it seems to me like a good idea to implementing features which help promote good FP patterns in a language that allows for both FP and OOP.

# Andreas Rossberg (7 years ago)

On 28 November 2012 14:39, Marius Gundersen <gundersen at gmail.com> wrote:

On Wed, Nov 28, 2012 at 1:20 PM, Andreas Rossberg <rossberg at google.com> wrote:

First, your definition of "pure" is not quite correct. Any function that even returns locally created state in some form (i.e., a new object), is impure.

Fair enough. A better name would probably be side effect free functions.

Well, observable allocation of state, or assignment to parameters, are side effects.

On the other hand, accessing non-local bindings that are (deeply) immutable does not constitute an effect, yet you want to forbid it.

Seriously, what is the use case for this rather strange definition?

Last, short of a static type-and-effect system, I don't see how the necessary checks could be implemented without imposing significant overhead on almost every primitive operation -- because every function, and hence almost any piece of code, might potentially end up with a "pure" function in its call chain, and would need to check for that.

I'm not an implementer of EcmaScript, so I don't have deep knowledge of how this could be implemented. I would imagine that a subset of a true pure function, where the only restriction would be that only variables passed as arguments to the function exist in the scope, would be relatively easy to implement. Wouldn't this be a faster implementation than todays functions, which have to keep track of scope? These side-effect-free functions would only need to contain the variables passed as parameters in their scope. Accessing anything outside the scope would result in a reference error.

Modern JS implementations don't do the kind of runtime bookkeeping of scopes that you seem to assume. Compiled code doesn't even know what a scope is, it just accesses hardcoded indices into the stack or into some heap arrays.

# Jussi Kalliokoski (7 years ago)

On Wed, Nov 28, 2012 at 3:47 PM, Marius Gundersen <gundersen at gmail.com>wrote:

On Wed, Nov 28, 2012 at 1:21 PM, David Bruant <bruant.d at gmail.com> wrote:

Hi Marius,

I won't say the idea is bad, but what would be the benefit of this new type of function?

From experience on this list, if a new idea cannot prove to make a major difference with what currently exists, it is not considered to be added to the ES6 spec. The major difference can be in performance, security, language extensibility, programming idioms/conveniences, etc. Do you have reasons to think pure functions as you propose them make that big of an improvement as opposed to JS as it is?

With many new functional programming possibilities (like map, reduce, filter, lambdas) there are many scenarios where the implementer should use pure (or as I renamed it in another reply, side-effect-free) functions. Library implementers are likely interested in making functions that can take lambdas as parameters, and these lambdas (in many cases) should not alter any outside state, they should only return a value. A function with this restriction could run faster and take up less memory since it only needs the values passed to it as arguments in its own scope.

Mostly I feel this would introduce better coding practises with a focus on functional programming rather than object oriented programming. Using functions with limited scope would reduce the number of variables written to the global scope, and would reduce the amount of state in an application. Seeing as FP is a bit of a trend today (due, in part, to the popularity of JavaScript), it seems to me like a good idea to implementing features which help promote good FP patterns in a language that allows for both FP and OOP.

With pure function, are you after

a) The equivalent of inline in the C family? b) Something less state-independent, a function that could be, for example, parallelized/forked safely?

For a), I'm not sure to which extent implementations already do this. For b), seems like something related to the RiverTrail project.

# David Bruant (7 years ago)

Le 28/11/2012 14:47, Marius Gundersen a écrit :

On Wed, Nov 28, 2012 at 1:21 PM, David Bruant <bruant.d at gmail.com <mailto:bruant.d at gmail.com>> wrote:

Hi Marius,

I won't say the idea is bad, but what would be the benefit of this
new type of function?

From experience on this list, if a new idea cannot prove to make a
major difference with what currently exists, it is not considered
to be added to the ES6 spec.
The major difference can be in performance, security, language
extensibility, programming idioms/conveniences, etc.
Do you have reasons to think pure functions as you propose them
make that big of an improvement as opposed to JS as it is?

With many new functional programming possibilities (like map, reduce, filter, lambdas) there are many scenarios where the implementer should use pure (or as I renamed it in another reply, side-effect-free) functions.

Why "should"? What is the problem if people don't?

Library implementers are likely interested in making functions that can take lambdas as parameters, and these lambdas (in many cases) should not alter any outside state, they should only return a value. A function with this restriction could run faster and take up less memory since it only needs the values passed to it as arguments in its own scope. "could run faster and take up less memory". I agree it "could", but it's not given it will when implemented. In some cases, it's possible to prove that a function is side-effect free, but to the best of my knowledge, no implementations has gone to length to do such thing, probably, because the cost is too big in regard to the benefits.

Mostly I feel this would introduce better coding practises with a focus on functional programming rather than object oriented programming.

I'm only half sympathic to this argument. JS is a language with many users with different programming styles. I understand the benefits of FP, but don't see why it should be favored over any other style.

Using functions with limited scope would reduce the number of variables written to the global scope

I use both OOP and FP styles and I don't see how FP would help for global variables. Basically, declaring variables as function local (with the "var" keyword) prevent global leaks.

and would reduce the amount of state in an application. ...and increase the use of temporary values and GC to compensate, because JS engines are not really written to optimize for FP? For instance, I think, none do tail call optimization yet? (others in the list know better on that point)

Seeing as FP is a bit of a trend today (due, in part, to the popularity of JavaScript), it seems to me like a good idea to implementing features which help promote good FP patterns in a language that allows for both FP and OOP.

You mentioned ReferenceError when the functions tries to reach a variable outside of its scope. I think it's possible to implement such a thing in current JavaScript. Take a look at the Caja code [1] for inspiration (sorry the barrier is a bit high as I put it. Ask me questions if you need help). Also, to avoid being tempted to use a scope, just define your function as non-nested (global :-s or directly inside an IIFE (Immediately Invoked Function Expression))

Just a note: [email protected] f(x){ x.a = 1; } I think this is a side-effect. Should an error be thrown here too? If so, does it mean you can't use objects as arguments?

In any case, none of what you answered really convinces me (just to clarify, I just hang out on the list, I make no decisions with regard to the language) as being a major improvement for the language. It would be a good convenience for FP style, that's it.

David

[1] code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/startSES.js#635

# Andrea Giammarchi (7 years ago)

I use the context argument quite a lot in order to be able to recycle those functions as much as possible so this is needed in my case plus map and filter do not necessary return a new value based on what's in the function.

"abc".split("").map(function(c){return this(c.charCodeAt(0) - 32)}, String.fromCharCode).join("") // ABC

whenever it's a dumb example or not, I don't see that function dangerous, not serializable, or not pure

my 2 cents

# Claus Reinke (7 years ago)

With many new functional programming possibilities (like map, reduce, filter, lambdas) there are many scenarios where the implementer should use pure (or as I renamed it in another reply, side-effect-free) functions. Why "should"? What is the problem if people don't?

In the non-sequential versions, side-effect-free operators give more flexibility in scheduling, eg, splitting an array into sub-arrays, running partial loops on separate processors, then combine the results.

In sequential code, side-effect-free operators give more flexibility in refactoring and simplify program understanding (code segments without access to global state have smaller APIs).

As pointed out by Andreas, achieving that is not easy in JS. Even when some kind of soft or gradual type system finally takes over the JS world,-) it'll still have to deal with a lot of needlessly side-effect-based APIs. So my preference would be to make sure that ES.next has side-effect-free, expression-level APIs next to the existing side-effect-based, statement-level APIs, wherever possible (starting with object updating/extension).

The definition used by Marius seems to call for closed functions (no non-local variables), which is not directly related. It sounds as if he wants to allow local side-effects, as long as they do not propagate outside the local call stack, but that is not as simple as using closed functions. Andreas provided counter-examples.

Claus

# David Bruant (7 years ago)

Le 28/11/2012 18:19, Claus Reinke a écrit :

With many new functional programming possibilities (like map, reduce, filter, lambdas) there are many scenarios where the implementer should use pure (or as I renamed it in another reply, side-effect-free) functions. Why "should"? What is the problem if people don't?

In the non-sequential versions, side-effect-free operators give more flexibility in scheduling, eg, splitting an array into sub-arrays, running partial loops on separate processors, then combine the results.

I agree, but it's far from being applicable in JS apparently (I see you agree later in your response). I suggested this as my very first Bugzilla bug. I encourage to read the comments: bugzilla.mozilla.org/show_bug.cgi?id=593706

# Oliver Hunt (7 years ago)

Just adding an implementers point of view: Adding a new keyword/annotation is risky and/or complicates language semantics -- we don't want to break existing code, so new keywords are hard, likewise context dependent keywords can be tricksy: "f = function pure(){}" means what?

Assuming that there is an annotation that says a function is pure, runtime enforcement becomes either very expensive very quickly (possibly tending towards the halting problem in the general case), or can randomly throw exceptions. Your sum function is a trivial case: function sum(a,b) { return a + b } // I'm pure!!!!

We want to ensure sum has no side effects so we either typecheck on entry to ensure we will not need make a function call for the a and b typeconversions, or we throw on the first attempt to make a typeconversion (the exception will occur before any side effects, so you're still "safe" just annoyed).

But what about: function bizzaroSum(a, b) { while (a.b) a = a .b; return a + b; } ?

An ahead of time type check has unbounded time complexity, so there is no reason to not just execute and throw on a side effecting operation.

(Also what if a.b is a getter that simply returns 1 - does that count as a side effect because of the implied call, or does it not? after all there's not actually any side effect here)

So the only efficient way of dealing with a "pure" function is to run the code and perform type checks as you go to ensure that no calls are happening.

This gets us to almost the level of performance modern JS engines are already getting, without having any concept of "pure". If your code is doing sensible things, and the types are consistent every supposed gain you get from "purity" is already there.

But your purity constraints means no side-effects, no general case calls, neither of these limits is helpful to modern JS engines as they will happily inline functions anyway regardless of purity, and will just do a stack rewrite and fallback to general code if their assumptions turn out to be false.

As an aside, the performance issues JSC at least has with map, reduce, etc is VM re-entry. For us that's a much more significant performance problem than code gen quality.

# Waldemar Horwat (7 years ago)

On Wed, Nov 28, 2012 at 5:39 AM, Marius Gundersen <gundersen at gmail.com>wrote:

On Wed, Nov 28, 2012 at 1:20 PM, Andreas Rossberg <rossberg at google.com>wrote:

Second, due to the extremely impure nature of JavaScript, there aren't many useful pure functions you could even write. For example, your 'sum' function is not pure, because the implicit conversions required by + can cause arbitrary side effects.

Functions passed to the array methods map, reduce, filter, etc would be good candidates for pure/side-effect-free functions. These functions shouldn't alter any state; they should only return a new value based on the parameter they were sent.

You haven't addressed Andreas's point: Almost any function you write is nonpure, including your sum example. As a fun exercise, go ahead and write a pure version of your sum example.

Waldemar
# Oliver Hunt (7 years ago)

On Nov 28, 2012, at 12:25 PM, Waldemar Horwat <waldemar at google.com> wrote:

On Wed, Nov 28, 2012 at 5:39 AM, Marius Gundersen <gundersen at gmail.com> wrote: On Wed, Nov 28, 2012 at 1:20 PM, Andreas Rossberg <rossberg at google.com> wrote: Second, due to the extremely impure nature of JavaScript, there aren't many useful pure functions you could even write. For example, your 'sum' function is not pure, because the implicit conversions required by + can cause arbitrary side effects.

Functions passed to the array methods map, reduce, filter, etc would be good candidates for pure/side-effect-free functions. These functions shouldn't alter any state; they should only return a new value based on the parameter they were sent.

You haven't addressed Andreas's point: Almost any function you write is nonpure, including your sum example. As a fun exercise, go ahead and write a pure version of your sum example.

Waldemar

Here you go:

function sum(a, b) { var undefined; switch (typeof a) { case "number": case "string": break; default: return +undefined; } switch (typeof b) { case "number": case "string": break; default: return +undefined; } return a + b; }

# David Bruant (7 years ago)

Le 28/11/2012 21:35, Oliver Hunt a écrit :

On Nov 28, 2012, at 12:25 PM, Waldemar Horwat <waldemar at google.com <mailto:waldemar at google.com>> wrote:

On Wed, Nov 28, 2012 at 5:39 AM, Marius Gundersen <gundersen at gmail.com <mailto:gundersen at gmail.com>> wrote:

On Wed, Nov 28, 2012 at 1:20 PM, Andreas Rossberg
<rossberg at google.com <mailto:rossberg at google.com>> wrote:

    Second, due to the extremely impure nature of JavaScript,
    there aren't
    many useful pure functions you could even write. For example,
    your
    'sum' function is not pure, because the implicit conversions
    required
    by + can cause arbitrary side effects.


Functions passed to the array methods map, reduce, filter, etc
would be good candidates for pure/side-effect-free functions.
These functions shouldn't alter any state; they should only
return a new value based on the parameter they were sent.

You haven't addressed Andreas's point: Almost any function you write is nonpure, including your sum example. As a fun exercise, go ahead and write a pure version of your sum example.

Waldemar

Here you go:

function sum(a, b) { var undefined; switch (typeof a) { case "number": case "string": break; default: return +undefined; } switch (typeof b) { case "number": case "string": break; default: return +undefined; } return a + b; }

I don't even... Reading this makes me understand why "return a+b" didn't work and... oh well... JavaScript is quite a language...

# Oliver Hunt (7 years ago)

On Nov 28, 2012, at 1:11 PM, David Bruant <bruant.d at gmail.com> wrote:

Le 28/11/2012 21:35, Oliver Hunt a écrit :

On Nov 28, 2012, at 12:25 PM, Waldemar Horwat <waldemar at google.com> wrote:

On Wed, Nov 28, 2012 at 5:39 AM, Marius Gundersen <gundersen at gmail.com> wrote: On Wed, Nov 28, 2012 at 1:20 PM, Andreas Rossberg <rossberg at google.com> wrote: Second, due to the extremely impure nature of JavaScript, there aren't many useful pure functions you could even write. For example, your 'sum' function is not pure, because the implicit conversions required by + can cause arbitrary side effects.

Functions passed to the array methods map, reduce, filter, etc would be good candidates for pure/side-effect-free functions. These functions shouldn't alter any state; they should only return a new value based on the parameter they were sent.

You haven't addressed Andreas's point: Almost any function you write is nonpure, including your sum example. As a fun exercise, go ahead and write a pure version of your sum example.

Waldemar

Here you go:

function sum(a, b) { var undefined; switch (typeof a) { case "number": case "string": break; default: return +undefined; } switch (typeof b) { case "number": case "string": break; default: return +undefined; } return a + b; } I don't even... Reading this makes me understand why "return a+b" didn't work and... oh well... JavaScript is quite a language...

my favourite bit in all of this was var undefined; ... return +undefined;

Although i realise that 0/0 is possibly going to be a more efficient way to get NaN :D

# Brendan Eich (7 years ago)

David Bruant wrote:

I don't even... Reading this makes me understand why "return a+b" didn't work and... oh well... JavaScript is quite a language...

I said at JSConf.au that parts of JS are like an old-school Adventure game. Sometimes you get eaten by a grue.

# Axel Rauschmayer (7 years ago)

Would returning NaN be impure (apart from running the risk of it having been changed to something different, globally)?

# Oliver Hunt (7 years ago)

IIRC NaN is readonly, but you'd need to be able to guarantee the lexical scope between your function definition and the global object did have any shadowing :D

# Brendan Eich (7 years ago)

Axel Rauschmayer wrote:

Would returning NaN be impure (apart from running the risk of it having been changed to something different, globally)?

You can't say "apart from...", that's exactly the risk. It could be replaced with a global getter that has effects.

Isolating a pure function to a sandbox or prepared global environment saves this, but then the function's "pure" annotation can't be enough to express what's required.

# Axel Rauschmayer (7 years ago)

Ah, global getter. Didn’t think of that.

# Hudson, Rick (7 years ago)

Function purity, while an interesting concept, probably isn't as important as the function being associative. Our work with Parallel EcmaScript (aka River Trail) strawman:data_parallelism has shown that for a useful set of kernel functions the compiler can conservatively determine that a function does not modify non-local variables. We have found no need for function annotation, instead if one passes a function to a ParallelArray method we trust but verify that it modifies only local variables. Beyond this check we do not attempt to prove that a function is associative. The results are guaranteed only to be the result of some ordering and never return an "out of thin air" value. Floating point anomalies aside, if the function is associative the result will be deterministic.