Functional Operators

# Vihan Bhargava (7 years ago)

As JS/ECMAScript gains more and more of a functional use, I've been really wanting a functional operator feature from JS for a while. If you aren't familiar with them, they are inspired from haskell along the lines of:

let add1 = (+);  // This is the same as below
let add2 = (a, b) => a + b;

This definitely helps clear up verbosity in reduce statements and related functions. Another example would be for sorting:

[1,4,3,6].sort((-));

While all operators (namely =) wouldn't work as functional operators, I think this would be a useful addition to the language.

# Darien Valentine (7 years ago)

Minor point regarding the syntax given here: introducing (/) would likely be problematic because it breaks the constraint that there are no positions in the grammar where both a division operator and a regular expression literal could be valid continuations (unless you mean for (+), (/), etc to each individually be new atomic tokens, no whitespace).

(Perhaps new built-ins like Math.add etc might represent a more consistent approach to the issue of operators not being function references?)

# kai zhu (7 years ago)

-1 i can see this becoming a debugging nightmare in math libraries where its confused with arithmetic expressions.

yet-another-alien-syntax to remember when debugging other people's code.

# Isiah Meadows (7 years ago)

+1 on the idea, -1 on the execution. Here's a few things to be aware of:

  • A single unary or binary operator without parentheses is unambiguous where only expressions are allowed.
  • If the operators directly desugar to lambdas, you'll have functions that look ===, but aren't.
  • The operator - could be either unary or binary, and x => -x and `(a,

b) => a - bare both valid potential desugarings. Similar issues exist for+`.

# Ron Buckton (7 years ago)

I have been looking into functional operators while working on a proposal for pipeline and partial application. I’ve found that a sigil like {+} is just as ergonomic as (+), but has fewer lookahead issues with respect to regular expression parsing. While (/) is ambiguous as to whether it would be a division function or the start of a parenthesized regular expression literal, {/ is far less ambiguous in most expression positions. The only ambiguity is at the statement level where {/ could be interpreted as the start of a block with a regular expression literal. However, it is fairly unlikely this expression would be used in this position, and this can be mitigated using parentheses just as we do for object assignment patterns in destructuring assignments.

The other ambiguous case is how to differentiate between overloaded binary and unary operators. For that, I’ve considered following the approach taken by F# and prefixing overloaded unary operators with tilde. As such {+} would always be a binary plus function, while {~+} would be the unary plus function. In the same vein, {-} would be binary minus, while {~-} would be the unary minus function. For non-overloaded unary operators the prefix is unnecessary, so {~} and {!} would not be prefixed.

While built-ins could serve this case, they are far less ergonomic than a shorthand sigil for an operator. On the other hand, we could have both, with the operator sigils acting as shorthand for the long-form built-in methods. Either way, I would expect {+} === {+} as there is no sense in allocating a fresh function object each time it is encountered. Ideally, these would be frozen functions that are created once per realm and have the same semantics as an arrow function (i.e. [[Call]] but no [[Construct]], etc.).

Ron

From: es-discuss [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Darien Valentine Sent: Monday, July 10, 2017 3:08 PM To: es-discuss at mozilla.org Subject: Re: Re: Functional Operators

Minor point regarding the syntax given here: introducing (/) would likely be problematic because it breaks the constraint that there are no positions in the grammar where both a division operator and a regular expression literal could be valid continuations.

(Perhaps new built-ins like Math.add etc might represent a more consistent approach to the issue of operators not being function references?)

# Darien Valentine (7 years ago)

If I understand right, Ron, it means a new RHS for PrimaryExpression which would behave like a reference, except that it is (presumably) not a valid assignment target? Is {+}.length, etc, then valid? Can you explain more about the ergonomics — maybe it’s just from lack of familiarity, but to me this seems pretty grawlixy, like something you’d see in Perl.

In other words, I’m unsure how arr.reduce({+}, 0) is more ergonomic than arr.reduce(Math.add, 0)*. Assuming it is and I’m just failing to see it, is the benefit significant enough to merit new syntax?

(On further consideration, maybe Reflect.add, since + is not specific to numeric values...)

# Ron Buckton (7 years ago)

(apologies for top posting as I’m replying from my phone)

Functional operators are not very interesting on their own, but are much more interesting in terms of pipelines and partial application. However, they may be a stretch in either scenario, so I’m not expecting them to be part of any official proposal for pipeline/partial application at this time.

By ergonomic, I meant that ‘{+}’ is far fewer characters than ‘Math.add’. Also, not every ‘+’ is arithmetic (i.e. string concatenation).

I can imagine a world where I can do:

const sum = numbers |> reduce(?, {+});

const joined = strings |> reduce(?, {+});

As a shorthand for:

const sum = numbers |> reduce(?, (a, b) => a + b);

const joined = strings |> reduce(?, (a, b) => a + b);

(I’m using |> here for pipeline and ? as a positional argument for partial application here)

Ron

From: Darien Valentine<mailto:valentinium at gmail.com>

Sent: Sunday, July 16, 2017 1:18 AM To: es-discuss at mozilla.org<mailto:es-discuss at mozilla.org>

Subject: Re: Re: Functional Operators

If I understand right, Ron, it means a new RHS for PrimaryExpression and would behave like a reference, except that it is (presumably) not a valid assignment target? Can you explain more about the ergonomics — maybe it’s just from lack of familiarity, but to me this seems pretty grawlixy, like something you’d see in Perl.

In other words, I’m unsure how arr.reduce({+}) is more ergonomic than arr.reduce(Math.add)*. Assuming it is and I’m just failing to see it, is the benefit significant enough to merit new syntax?

(On further consideration, maybe Reflect.add, since + is not specific to numeric values...)

On Sun, Jul 16, 2017 at 2:19 AM, Ron Buckton <Ron.Buckton at microsoft.com<mailto:Ron.Buckton at microsoft.com>> wrote:

I have been looking into functional operators while working on a proposal for pipeline and partial application. I’ve found that a sigil like {+} is just as ergonomic as (+), but has fewer lookahead issues with respect to regular expression parsing. While (/) is ambiguous as to whether it would be a division function or the start of a parenthesized regular expression literal, {/ is far less ambiguous in most expression positions. The only ambiguity is at the statement level where {/ could be interpreted as the start of a block with a regular expression literal. However, it is fairly unlikely this expression would be used in this position, and this can be mitigated using parentheses just as we do for object assignment patterns in destructuring assignments.

The other ambiguous case is how to differentiate between overloaded binary and unary operators. For that, I’ve considered following the approach taken by F# and prefixing overloaded unary operators with tilde. As such {+} would always be a binary plus function, while {~+} would be the unary plus function. In the same vein, {-} would be binary minus, while {~-} would be the unary minus function. For non-overloaded unary operators the prefix is unnecessary, so {~} and {!} would not be prefixed.

While built-ins could serve this case, they are far less ergonomic than a shorthand sigil for an operator. On the other hand, we could have both, with the operator sigils acting as shorthand for the long-form built-in methods. Either way, I would expect {+} === {+} as there is no sense in allocating a fresh function object each time it is encountered. Ideally, these would be frozen functions that are created once per realm and have the same semantics as an arrow function (i.e. [[Call]] but no [[Construct]], etc.).

Ron

From: es-discuss [mailto:es-discuss-bounces at mozilla.org<mailto:es-discuss-bounces at mozilla.org>] On Behalf Of Darien Valentine

Sent: Monday, July 10, 2017 3:08 PM To: es-discuss at mozilla.org<mailto:es-discuss at mozilla.org>

Subject: Re: Re: Functional Operators

Minor point regarding the syntax given here: introducing (/) would likely be problematic because it breaks the constraint that there are no positions in the grammar where both a division operator and a regular expression literal could be valid continuations.

(Perhaps new built-ins like Math.add etc might represent a more consistent approach to the issue of operators not being function references?)

# doodad-js Admin (7 years ago)

One question I have: why all these fuzzy (sorry, functional) operators? That could become very hard to know what the code exactly does, and difficult to debug... Are you becoming too "lazy" to type on the keyboard?

From: Ron Buckton [mailto:Ron.Buckton at microsoft.com] Sent: Sunday, July 16, 2017 3:54 PM To: Darien Valentine <valentinium at gmail.com>; es-discuss at mozilla.org

Subject: RE: Re: Functional Operators

(apologies for top posting as I'm replying from my phone)

Functional operators are not very interesting on their own, but are much more interesting in terms of pipelines and partial application. However, they may be a stretch in either scenario, so I'm not expecting them to be part of any official proposal for pipeline/partial application at this time.

By ergonomic, I meant that '{+}' is far fewer characters than 'Math.add'. Also, not every '+' is arithmetic (i.e. string concatenation).

I can imagine a world where I can do:


const sum = numbers |> reduce(?, {+});

const joined = strings |> reduce(?, {+});

As a shorthand for:


const sum = numbers |> reduce(?, (a, b) => a + b);

const joined = strings |> reduce(?, (a, b) => a + b);

(I'm using |> here for pipeline and ? as a positional argument for

partial application here)

Ron

From: Darien Valentine <mailto:valentinium at gmail.com>

Sent: Sunday, July 16, 2017 1:18 AM To: es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>

Subject: Re: Re: Functional Operators

If I understand right, Ron, it means a new RHS for PrimaryExpression and would behave like a reference, except that it is (presumably) not a valid assignment target? Can you explain more about the ergonomics - maybe it's just from lack of familiarity, but to me this seems pretty grawlixy, like something you'd see in Perl.

In other words, I'm unsure how arr.reduce({+}) is more ergonomic than arr.reduce(Math.add)*. Assuming it is and I'm just failing to see it, is the benefit significant enough to merit new syntax?

(On further consideration, maybe Reflect.add, since + is not specific to numeric values...)

On Sun, Jul 16, 2017 at 2:19 AM, Ron Buckton <Ron.Buckton at microsoft.com <mailto:Ron.Buckton at microsoft.com> > wrote:

I have been looking into functional operators while working on a proposal for pipeline and partial application. I've found that a sigil like {+} is just as ergonomic as (+), but has fewer lookahead issues with respect to regular expression parsing. While (/) is ambiguous as to whether it would be a division function or the start of a parenthesized regular expression literal, {/ is far less ambiguous in most expression positions. The only ambiguity is at the statement level where {/ could be interpreted as the start of a block with a regular expression literal. However, it is fairly unlikely this expression would be used in this position, and this can be mitigated using parentheses just as we do for object assignment patterns in destructuring assignments.

The other ambiguous case is how to differentiate between overloaded binary and unary operators. For that, I've considered following the approach taken by F# and prefixing overloaded unary operators with tilde. As such {+} would always be a binary plus function, while {~+} would be the unary plus function. In the same vein, {-} would be binary minus, while {~-} would be the unary minus function. For non-overloaded unary operators the prefix is unnecessary, so {~} and {!} would not be prefixed.

While built-ins could serve this case, they are far less ergonomic than a shorthand sigil for an operator. On the other hand, we could have both, with the operator sigils acting as shorthand for the long-form built-in methods. Either way, I would expect {+} === {+} as there is no sense in allocating a fresh function object each time it is encountered. Ideally, these would be frozen functions that are created once per realm and have the same semantics as an arrow function (i.e. [[Call]] but no [[Construct]], etc.).

Ron

From: es-discuss [mailto:es-discuss-bounces at mozilla.org <mailto:es-discuss-bounces at mozilla.org> ] On Behalf Of Darien Valentine

Sent: Monday, July 10, 2017 3:08 PM To: es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>

Subject: Re: Re: Functional Operators

Minor point regarding the syntax given here: introducing (/) would likely be problematic because it breaks the constraint that there are no positions in the grammar where both a division operator and a regular expression literal could be valid continuations.

(Perhaps new built-ins like Math.add etc might represent a more consistent approach to the issue of operators not being function references?)