Swift style syntax

# Mohsen Azimi (4 years ago)

Is it possible to extend JavaScript syntax to support Swift style block syntax[1]?

In Swift it's possible to omit return keyword


   1. reversed = names.sort( { s1, s2 in s1 > s2 } )

or omit argument declaration like this:


   1. reversed = names.sort( { $0 > $1 } )

or apply an operator to arguments of a function


   1. reversed = names.sort(>)

We have the first feature in ES2015 already:

let sorted = names.sort((a, b)=> a > b);

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)

[1][ developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html ]

# Alexander Jones (4 years ago)

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!)

# Caitlin Potter (4 years ago)

In the case of sorting, are arrow functions not good enough? Or are we really asking for full continuation support

# Claude Pache (4 years ago)

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.

# Frankie Bagnardi (4 years ago)

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).

# Andrea Giammarchi (4 years ago)

... sort of (no pun intended)

let sorted = names.sort((...$) => $[0] > $[1]);
# Isiah Meadows (4 years ago)

Interesting trick, Andrea. Never thought of that before.

# Mohsen Azimi (4 years ago)

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)
# Peter van der Zee (4 years ago)

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.

# Thomas (4 years ago)

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.

# Andrea Giammarchi (4 years ago)

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
});
# Mohsen Azimi (4 years ago)

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]
# Jordan Harband (4 years ago)

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.

# Alexander Jones (4 years ago)

Operator.plus? I'd be totally ok with that.

# Isiah Meadows (4 years ago)

+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.

# Andreas Rossberg (4 years ago)

All this is well-known from functional languages, with well-known solutions. The only real problem is:

let f = (-)

Is this unary or binary -?

# Isiah Meadows (4 years ago)

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).

# Steve Fink (4 years ago)

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 to x => 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

# Isiah Meadows (4 years ago)

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, +).

# Mohsen Azimi (4 years ago)

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

# Isiah Meadows (4 years ago)

Ok... I don't have a device to properly learn Swift with, as I don't own or have easy access to a Mac.

# Waldemar Horwat (4 years ago)

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's list.sort(>) and list.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
# Dawid Szlachta (4 years ago)

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>:

# Michael McGlothlin (4 years ago)

It'd be simple to just define all operators as function​s 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.

# Viktor Kronvall (4 years ago)

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>:

# Yongxu Ren (4 years ago)

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(>)
# Isiah Meadows (4 years ago)

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.

# Isiah Meadows (4 years ago)

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 from imul)

  • 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?

# Tab Atkins Jr. (4 years ago)

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, where Math.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!

# Isiah Meadows (4 years ago)

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, not x => ToNumber(x). The

behavior is slightly different, but can be observable without modifying Number, I believe.

# Michael McGlothlin (4 years ago)

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.

# Isiah Meadows (4 years ago)

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.

# Bob Myers (4 years ago)

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

# Isiah Meadows (4 years ago)

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.

# Ates Goral (4 months ago)

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 from imul)
  • 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

# Michael Luder-Rosefield (4 months ago)

Dammit babies, you've got to be kind.

# Michael Luder-Rosefield (4 months ago)

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.