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