In my previous post, I suggested tail nests as a profitable target for reducing reducing parens and braces that distract from common code patterns. I made two concrete suggestions, to
make both braces around function bodies and parens around
function applications optional.

The combination of both suggestions would allow to reduce
the nesting of parens and braces in tail nests, such as nested callbacks (function definition nested in function application
argument nested in ..). For the running example, this was
successful, but rather odd looking - it should be possible
to do better.

So I would like to modify those suggestions, to bring them more in line with Javascript practice and Harmony directions.
As a bonus, the modified suggestions simplify another common
case of nesting, namely curried function definitions.

[mnemonic summary of old suggestions, to be modified]

Suggestion 1 (optional braces around function bodies):
   '(function (..) ..)' may be used instead of 'function (..) {..}'

Suggestion 2 (optional parens around function applications):

   '(f @ a1, .. , an)' may be used instead of  'f(a1,..,an)'

Suggestion 2 is especially problematic if the argument list
has more than one component: the hope was that we might later get rid of the commas as well, assuming that argument lists are argument tuples, which could become individual arguments by currying. However, I've come to think that this underlying assumption does not fit Javascript: With optional arguments and unbounded lengths, argument lists are really argument arrays - they do not stand for multiple arguments (Javascript has curried functions for that), they each stand for a single argument with an unspecified number of components. We just use them for multiple arguments because
curried function definitions are so awkward.

Harmony changes, such as formal parameter destructuring and spreads, will remove the differences in feature sets between
argument lists and arrays, making it possible to replace

   'f(a1,..,an)' and 'function f(a1,..,am) {..}'

with
   'f([a1,..,an])' and 'function f([a1,..,am]) {..}'

at which point the parens will be redundant and each argument
list can be a proper array (dear arguments: I want my syntax back!-). So this part is well in hand already, and trying to break up those argument list arrays in other ways would go against the direction Harmony is taking.

Also, the language already provides for curried function definitions and applications, they just don't get used much
yet. Curried applications are easy, but curried definitions
happen to be very awkward, syntactically.

My modified suggestions take both curried functions and
argument lists as arrays into account, so they do a little less work on applications, making better use of existing syntax, and a little more work on definitions, hoping to give curried definitions a lift.

What I'd like is to be able to replace the horrible

   function (..) { return function (..) { .. }}

with the shorthand notation

   function (..)(..) { .. }

(and, similarly, '#(..)(..){..}' instead of '#(..){ #(..){..}}')

giving curried function definitions the same short syntax
as curried function applications. This shorthand makes it
obvious that each argument list really is just a single, complex argument to the function. There are a few obvious problems which would have made this difficult before
Harmony:

   - using the outer function's arguments pseudo-array
       in the inner function's body;
Harmony's spreads avoid that problem entirely.

   - using the outer function's 'this' in the inner function's
       body (the shorthand notation no longer has an outer
function body in which to rename 'this' to 'self'); this is being addressed in other threads here, so Harmony is likely to offer at least one solution
       (optional naming for the implicit argument 'this').

   - if the formal parameters can consist of multiple
       argument lists, the beginning of the function body
       is no longer unambiguous without those braces;
       to address this, we need an explicit syntactic marker
       at the boundary between arguments and body;

       from the archives, it seems that '=>' has been a
       fairly popular suggestion (usually instead of a prefix
       marker), so I'll adopt that, but without removing the
       prefix marker, be it 'function' or '#'.

Which results in the following modified suggestions

// --------------------------------------------------
// Modified suggestion 1 (function definitions): 1a (curried definition shorthand)
           function (..)(..) { .. }

       may be used instead of

           function (..) { return function (..) { .. }}

       for arbitrary nestings.

   1b (function bodies)

           (function (..)..(..) => ..)

may be used instead of
           function (..)..(..) {..}

       (the outer parens are _not_ part of the function definition
        syntax - they indicate that the source context delimits the
        function definition from the outside: if the construct in
which the definition is nested ends, so does the definition)
   1c (ASI needs to know about 1b's implicit block endings)

// --------------------------------------------------
// Modified suggestion 2 (function applications):

       (f @ x)

   may be used instead of

       f(x)

   (the outer parens are _not_ part of the function application
    syntax - they indicate that the source context delimits the
    function application from the outside: if the construct in
which the application is nested ends, so does the application) '@' is now just a left-associative infix operator (same precedence as function application), so

       (f @ x @ y @ z)

   is

       (((f @ x) @ y) @ z)

   which is

       f (x) (y) (z)

   (ideally, infix function application would just be juxtaposition,
     ie, the parens around single-component argument lists
     would be optional, without requiring an explicit operator)

// --------------------------------------------------

The effect on our running example, in terms of removing
redundant parens and braces, can be similar, but that
now needs support from the libraries: they would need to
curry their API functions so that the callback becomes a
second, separate argument (instead of the last component
in a single complex argument list).
(mainWindow.menu("File") @ function(file) =>
 file.openMenu @ function(menu) =>
   menu.item("Open") @  function(item) =>
     item.click @ function() =>
mainWindow.getChild(type('Window')) @ function(dialog) =>
             ...
);

// Note: since function definitions extend as far as possible,
// to the closing paren here, the inner '@' are unambiguosly
// nested

These modified suggestions are simpler, more in line with
Harmony's directions, and help to reduce the syntactic noise
for two common sources of nesting: curried definitions and
definitions as callback arguments.

Comments, please?-)
Claus

_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to