imperative vs functional APIs or: how to work with constant objects?

Claus Reinke claus.reinke at
Tue Jun 21 09:47:25 PDT 2011

Javascript data structure operations: some of the APIs are imperative, 
some are functional, some are mixed. Having functional equivalents
to all imperative data structure operations is necessary to support 
expression-oriented programming over constant data structures.

"imperative" here refers to operations that are designed to be used 
as statements, changing inputs by side-effect, while "functional" 
refers to operations designed to be used as expressions, leaving 
their inputs unchanged. A familiar example is the Array API, e.g.:

    array.concat([1])    - returns a modified copy of array, expression

    array.push(1)        - modifies array in place, "statement"

It does seem as if older API elements tend to be imperative while 
newer ones tend to be functional. However, not all imperative
operations have functional counterparts yet, and with increasing
focus on constant and frozen objects, the need for functional
operations is going to increase.

In particular, I have been wondering about how freezing objects
curtails what I am able to do with them. Having worked with pure
functional language for a long time, I really like the advantages of
frozen/constant objects, both for avoiding bugs and to enable
optimizations, but I'm not used to not having a full set of 
operations to work with such objects:

Example 1: constant functions, prototypes and toString

    The standard API for functions requires imperative operations
    for both prototypical inheritance and for string representation.

    const C() {}
    // C.prototype.toString = function() { return 'C()' }
    // C.toString = function() { return 'C()' }

Example 2: property update

    The standard API for object property update is imperative.

    const obj = Object.freeze({ age : 1, .. });
    const older_obj = // a modified copy of obj, with age : 2??

The standard APIs for these fundamental operations are imperative
and so are not available on frozen objects (this also applies to the
record strawman, for instance).

How does one achieve the effects of these operations on frozen
objects, by creating modified copies? For an expression-oriented 
style of programming where no object is ever modified in place, 
it seems one has to use a lower level API, perhaps by calling
Object.create and Object.freeze to implement some form of
Object.clone, then mixing in the update on the clone before 
freezing it. Or perhaps using proxies to achieve the effect..

The situation is somewhat similar to the declarative object 
initializers being worked on as object literal extensions, but 
those don't seem to help here.

What is the best way of doing functional/declarative/expression-
oriented object modification in ES5 or ES/next? Is there really an 
imperative-preferred flaw in the current design?



More information about the es-discuss mailing list