Swift style syntax
IMO this is a good idea. When it's abundantly clear from context, I've already been naming my arrow function params _ if singular, and _1, _2 etc if several. As always, picking some punctuation straight from the scarce and sacred set of remaining ASCII symbols is going to be tricky. (If only we could just go APL on this!)
In the case of sorting, are arrow functions not good enough? Or are we really asking for full continuation support
Le 11 oct. 2015 à 17:45, Mohsen Azimi <me at azimi.me> a écrit :
But for omitting argument declaration we need to find an alternative to $0, $1... since those are valid variable names in JS. Maybe we can use #0, #1... instead.
This is very useful for functional programming aspect of JS. For example in a filter function:
let passed = objs.filter(#0.passed)
Your syntax is ambiguous: Should your code be interpreted as:
let passed = objs.filter($0 => $0.passed)
or:
let passed = $0 => objs.filter($0.passed)
You need some syntax in order to delimitate the function.
I don't think there's much value in this. Also sort is a bad example because it'd look like this, and there's no nifty shortcut answer to it.
names.sort((a, b) => a < b ? 1 : a > b ? -1 : 0);
In most cases you save a couple characters, but you can just use x/y/a/b/f/g/n/xs/xss for variable names in arrow functions instead of the $0 (which would likely be \0 in js).
... sort of (no pun intended)
let sorted = names.sort((...$) => $[0] > $[1]);
Interesting trick, Andrea. Never thought of that before.
Your syntax is ambiguous: Should your code be interpreted as:
let passed = objs.filter($0 => $0.passed)
or:
let passed = $0 => objs.filter($0.passed)
I don't understand why you parsed it in the second way Claude?
A more real world like example would be this:
let chbx = Array.from(document.querySelectorAll('input[type="checkbox"]'));
let checkedBoxes = chbx.filter(#0.checked)
On Mon, Oct 12, 2015 at 10:12 AM, Mohsen Azimi <me at azimi.me> wrote:
Your syntax is ambiguous: Should your code be interpreted as:
let passed = objs.filter($0 => $0.passed)
or:
let passed = $0 => objs.filter($0.passed)
I don't understand why you parsed it in the second way Claude?
It's not about the "why". He's trying to tell you this code is ambiguous. The parser cares not about the why.
Look at the generic case; how could the parser know how #0
is scoped?
There is an ambiguity and you'll need some way of telling JS what the
scope is of that #0
. This is why there's an arrow. This is why you
need to wrap arguments in parenthesis: Disambiguation. Basically; try
to think of ways how your example should be translated. Then try to
see if they can be translated differently. If they can, then you
need to think of a way to disambiguate. In the above, having no
"function offset marker" (like what the arrow is) means you cannot
know where it starts.
On a personal two cents; ugh, changes in this proposal would only lead to even worse illegible code.
Is it possible to extend JavaScript syntax to support Swift style block syntax[1]?
In Swift it's possible to omit return keyword
reversed = names.sort( { s1, s2 in s1 > s2 } )
As you note below this is already possible in es6, and might I add, has much more intuitive syntax in Es6. The swift syntax looks like a list comprehension gone wrong.
or omit argument declaration like this:
reversed = names.sort( { $0 > $1 } )
I for one think this is a bad idea - use rest arguments instead. It's pretty terrible as far as readability goes, although I'd like to see more examples of it being used in Swift code.
or apply an operator to arguments of a function
reversed = names.sort(>)
This might actually be possible - I can't think of any ambiguous situations for passing operators as if they were first class functions. If it is possible, I'd like to see this done.
for "historical record" sake that silly trick works even in a more meaningful way with RegExp replacements :-)
// before
'str'.replace(/(some)(thing)/, function ($0, $1, $2) {
// boring $1 $2 like RegExp.$1 and RegExp.$2
});
// now
'str'.replace(/(some)(thing)/, (...$) => {
// we have $[1], $[2] ... yaiiii
});
reversed = names.sort(>) This might actually be possible - I can't think of any ambiguous
situations for passing operators as if they were first class functions. If it is possible, I'd like to see this done.
Yes, it would be really cool if operators can be used as function. For example in Swift you can do this:
let scores = [10, 13, 15, 8, 9, 19, 20, 4, 6];
let sum = scores.reduce(0, combine: +) // 104
Or this:
var bools = [true, false, false, true, true];
bools.filter(!) // [false, false]
For that, the question would arise: is scores.reduce((a, b) => a + b)
and
bools.filter(x => !x)
so troublesome to write that it's worth the added
complexity to the language? "it would be nice" generally doesn't outweigh increased implementation and maintenance cost to implementors, learners, tool creators, etc.
Operator.plus? I'd be totally ok with that.
+1 for operators as functions (I frequently is them in languages that have
them), but there is an ambiguous case that frequently gets me: does (-)
represent subtraction or negation. It's usually the former in languages
with operators as functions.
But here's a couple other potential syntactical ambiguities, dealing with ASI:
// Is this `x => f(x)` or `x = (>); f(x)`
x =>
f(x)
// Is this `-x` or `-; x`?
-
x
Those can be addressed with a cover production to be used for expression statements and direct value assignment, requiring parentheses to clarify the latter case in each.
A similar ambiguity problem, arguably harder to resolve, is partially
applied subtraction, such as (- 2)
. Is that a -2 or is it equivalent to
x => x - 2
? I will caution on this idea, as I know that's the next
logical step.
All this is well-known from functional languages, with well-known solutions. The only real problem is:
let f = (-)
Is this unary or binary -
?
I failed to mention that clearly enough... In most functional languages, that's binary. And I think that should be the same for JS. It's surprising otherwise.
I'd say the closest precedent here I know of would be LiveScript, where the
operator is treated as binary when used as a function. Most functional
languages have separate functions for subtraction and negation, including
Haskell (-
vs negate
) and OCaml (-
and .-
vs negate
and
fnegate
).
On 10/12/2015 11:06 PM, Isiah Meadows wrote:
+1 for operators as functions (I frequently is them in languages that have them), but there is an ambiguous case that frequently gets me: does
(-)
represent subtraction or negation. It's usually the former in languages with operators as functions.But here's a couple other potential syntactical ambiguities, dealing with ASI:
// Is this `x => f(x)` or `x = (>); f(x)` x => f(x) // Is this `-x` or `-; x`? - x
Those can be addressed with a cover production to be used for expression statements and direct value assignment, requiring parentheses to clarify the latter case in each.
A similar ambiguity problem, arguably harder to resolve, is partially applied subtraction, such as
(- 2)
. Is that a -2 or is it equivalent tox => x - 2
? I will caution on this idea, as I know that's the next logical step.
It it just me? I find all this talk of bare operators to be completely... uh, I'll go with "inadvisable".
I can believe that you could carve out an unambiguous path through the grammar. But (a) it's going the way of line noise, (b) it uses up lots of possibilities for future expansion on something that isn't all that useful in the first place, and (c) it seems to be choosing concise syntax over readability in a big way.
C++ has an 'operator' keyword (and even then it comes out pretty ugly
Steve, I have little problem with whatever ends up the case, as long as
it's shorter than (x, y) => x + y
. The current idea was inspired by
Swift's list.sort(>)
and list.reduce(0, +)
.
Isiah,
In Swift it's not allowed to assign an operator so the ambiguous case you brought up can be avoided by not allowing assigning operators.
let x = +; // not allowed
Or it's not allowed to store operators in dictionaries:
let dic = [
"add": +
];
The only place (as far as I know) you can pass operators as functions is in arguments list of a function call. I don't know why but it doesn't work with unary operators either:
let nums = [1,3,4];
numsnums.map(++) // error
Ok... I don't have a device to properly learn Swift with, as I don't own or have easy access to a Mac.
On 10/13/2015 10:27, Isiah Meadows wrote:
Steve, I have little problem with whatever ends up the case, as long as it's shorter than
(x, y) => x + y
. The current idea was inspired by Swift'slist.sort(>)
andlist.reduce(0, +)
.
I second the concern with this being far too full of hazards to carry its weight. Let's say you allow something like the list.reduce(0, +) syntax for the various arithmetic operators. Then we get to the fun ones:
list.reduce(0, /) /x
Oops, you've just started a regular expression.
Waldemar
Maybe it could be possible to allow operator-like characters to be included in the methods names? Like:
Math.>
It seems clear and readable. I believe it could be parsed easily, too. Transpilers could just change it to Math['>'].
Or, maybe allow to use some kind of new syntax (easy to parse for transpilers), to explicitly inform the parser we want to use operator as a function? Say:
myArray.sort(:>)
I think both propositions fix the problem Waldemar mentioned.
Cheers, Dawid
2015-10-13 21:56 GMT+02:00 Waldemar Horwat <waldemar at google.com>:
It'd be simple to just define all operators as functions and the actual
operator is just syntaxial sugar. And then if you wanted to pass the
operator you'd simply pass it's function around like you would any other
function. Even your Math.['>']
seems far safer than Math.>
or just >
but I'd vote for Math.greaterThan
as being the best name. Saving a couple
letters of typing isn't worth the price of hours more debugging.
but I'd vote for
Math.greaterThan
I agree with this. This solution also disambiguates negate and minus which is good. If we were to introduce operators as functions with syntax I would prefer if there was some syntax to know if the operator was a binary or unary function.
Regarding operators how does this proposal interact with the proposal for value types and operator overloading?
2015-10-15 23:08 GMT+02:00 Michael McGlothlin <mike.mcglothlin at gmail.com>:
I vote for operator overloading!
I think this is probably going to be a good way to do it :
//in local scope
let operator(**) = (lhs,rhs) => Math.pow(lhs,rhs);
//class method
Complex.prototype.operator(+) = function(lhs,rhs){
return new Complex(lhs.r+rhs.r,lhs.i+rhs.i);
}
//global, may not be an good idea
operator(+) = (lhs,rhs) => lhs*rhs;
//this will work for case above
names.sort(>)
Problem: that is currently a runtime ReferenceError, and it looks ugly. And operator overloading has already been floated around in the value types proposal. Having it available for everything isn't that great of an idea, but for at least value types, it's not a bad one. And the syntax/semantics here (in your email) is not very good. Also, what about classes?
class Pair {
constructor(x, y) {
this.x = x;
this.y = y;
}
operator(+)(other) {
return new Pair(this.x + other.x, this.y + other.y)
}
// etc.
}
I don't like that arbitrary behavior. It's non-obvious. -1 from me.
I like the idea, but could the function names be made a little
shorter? I'd like to at least save some characters on it. Example:
(a, b) => a > b
is 15 characters, where Math.greaterThan
is 16.
For comparison, I also have equivalent versions for the colon-preceded operators
Here's some naming ideas I have:
Equality/Inequality:
a > b
->Math.gt
->:<
a >= b
->Math.gte
->:<=
a < b
->Math.lt
->:>
a <= b
->Math.lte
->:>=
a === b
-> Math.eq->
:===`a !== b
->Math.neq
->:!==
a == b
->Math.looseEq
->:==
a != b
->Math.looseNeq
->:!=
Basic Math:
-
a + b
->Math.add
->:+
-
a - b
->Math.sub
->:-
-
a * b
->Math.mul
->:*
(different fromimul
) -
a / b
->Math.div
->:/
-
a ** b
->Math.pow
->:**
(already exists) -
+a
->Number(a)
(already exists) -
-a
->Math.neg(a)
Bitwise:
-
a & b
->Math.and
->:&
-
a | b
->Math.or
->:|
-
a ^ b
->Math.xor
->:^
-
a << b
->Math.shl
->:<<
-
a >> b
->Math.sar
->:>>
-
a >>> b
->Math.shr
->:>>>
-
~a
->Math.not(a)
->:~
Logical:
-
a && b
->Math.land
->:&&
-
a || b
->Math.lor
->:||
-
!a
->Math.lnot
->:!
Example:
// A very inefficient, inaccurate N by N matrix multiplication implementation
const part = (f, x) => y => f(x, y)
function mul1(a, b) {
return a.map((arow, i) =>
arow.map((ax, j) =>
b[j].map(brow => brow[i])
.map(part(:*, ax))
.reduce(:+, 0)))
}
function mul2(a, b) {
return a.map((arow, i) =>
arow.map((ax, j) =>
b[j].map(brow => brow[i])
.map(part(Math.mul, ax))
.reduce(Math.add, 0)))
}
What do you all think?
On Thu, Oct 15, 2015 at 11:12 PM, Isiah Meadows <isiahmeadows at gmail.com> wrote:
I like the idea, but could the function names be made a little shorter? I'd like to at least save some characters on it. Example:
(a, b) => a > b
is 15 characters, whereMath.greaterThan
is 16.For comparison, I also have equivalent versions for the colon-preceded operators
Here's some naming ideas I have: [snip]
At that point, though, the use of the Math namespace starts getting weird. Equality and Logical aren't mathematical; Bitwise mostly isn't either. (It can sometimes be used to do mathy things, like using shift to mul/div by a power of 2, but that's not its core purpose.)
Put them on a new Op namespace object, or into an Op built-in module. Even shorter, and clearer!
That might work, although these two might need edited for consistency:
- Math.pow === Op.pow (assuming
**
gets in) - Op.num should be similar to
x => +x
, notx => ToNumber(x)
. The
behavior is slightly different, but can be observable without modifying
Number
, I believe.
Having Op for all operator functions would be a good call. Probably better than adding Bitwise, Logic, etc just from the point of view of not filling up the namespace to much. I think short names would be fine so long as they were readable. So leftShift could be lShift but lsh is probably to mysterious. Even operator-like functions without an actual operator assigned would make sense I think so no reason not to include pow. A general purpose casting operator/func would be good I think but not sure if there is a point to specialized ones just to numbers, strings, and whatnot.
My operator names for the shifts were modeled after the typical assembly mnemonics.
<<
-shl
(logical left shift)>>
-sar
(arithmetic/sign propagating right shift)>>>
-shr
(logical right shift)
There is no arithmetic left shift, since it's effectively equivalent to the logical version IIRC.
My main wish is that the names should be short, or there would be no advantage of using them.
What has been the discussion if any about having "standard" libraries, with well-defined behavior and included in the engine, but which must be imported to use to avoid collisions and backward incompatibility? In theory the import could also result in new methods on standard prototypes. Eg
import 'std::Functional';
var _add = ((a, b) => a + b).currify();
import * as Fn from 'std::Functional';
var _add = Fn.currify((a, b) => a + b));
import currify from 'std::Functional';
var _add = currify((a, b) => a + b));
This approach could also possibly be used for Op
.
-- Bob
A standard module library is something I've been wanting in ECMAScript proper for a while, now. It doesn't necessarily have to be in ECMA-262 itself, but it would be nice to have somewhere. It's come up several times already in this list, even as far back as 2012, when the module with syntax concept was first thought of. It's just a fantasy that hasn't been realized yet. It would make new features a little easier to add, though, and I would actually rate a standard module implementation a little higher in importance than most of the more recent ideas, in my personal opinion.
This topic has been discussed a long time ago and has stalled on discussions of a standard library.
Coming back to the idea of adding some basic math operations directly on Math, there could be value in adding at least the following (from Isiah Meadows's post):
a + b
->Math.add
->:+
a - b
->Math.sub
->:-
a * b
->Math.mul
->:*
(different fromimul
)a / b
->Math.div
->:/
Math.add
: Among potentially other things, this could be useful for
an out-of-the-box, terse way to add up items in an array. So, instead
of:
array.reduce((sum, item) => sum + item);
Items could be added up with:
array.reduce(Math.add);
Math.sub
: Among potentially other things, this could be useful for
sorting numbers without having to define a custom comparator (i.e. almost as
an out-of-the-box riposte to the "JavaScript WTF" refrain of
lexicographically sorting numbers by using the default sort
implementation.) So, instead of:
array.sort((first, second) => first - second);
Numbers could be sorted with:
array.sort(Math.sub);
Math.mul
and Math.div
: I don't have a compelling use case in mind,
but I'd just throw these in for good measure / symmetry.
Ates
Dammit babies, you've got to be kind.
I've always been partial to operators merely being functions with infix/whatever sugar; having operators without there being a corresponding function seems deeply weird.
Dammit babies, you've got to be kind.
Is it possible to extend JavaScript syntax to support Swift style block syntax[1]?
In Swift it's possible to omit return keyword
or omit argument declaration like this:
or apply an operator to arguments of a function
We have the first feature in ES2015 already:
But for omitting argument declaration we need to find an alternative to $0, $1... since those are valid variable names in JS. Maybe we can use #0, .#1... instead.
This is very useful for functional programming aspect of JS. For example in a filter function:
[1][ developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html ]