New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggestion: Infix operators/functions #2319

Closed
aleksey-bykov opened this Issue Mar 12, 2015 · 26 comments

Comments

Projects
None yet
@aleksey-bykov
Copy link

aleksey-bykov commented Mar 12, 2015

Since it's very unlikely that the extension methods will ever be implemented in a call-site-rewrite manner, please consider adding infix operators to enable writing in functional style similarly to what can be done in Haskell:

Monoids

function '+'<a>(left: a[], right: a[]) : a[] {
   return left.concat(right);
}

[1, 2, 3] '+' [4, 5]; // [1, 2, 3, 4, 5]

Monads

function '>>='<a, b>(promise: Promise<a>, bind: (value: a) => Promise<b>) : Promise<b> {
   return promise.then(bind);
}
$.get('/get') '>>=' x => $.post('/save', x + 1)

Functors

function '.'<a, b, c>(inner: (value: a) => b, outer: (value: b) => c) : (value: a) => c {
   return function(value: a) : c {
      return outer(inner(value))
   }
}

function f(x: X) : Y { }
function g(y: Y) : Z { }
(f '.' g)(x); // z
@danquirk

This comment has been minimized.

Copy link
Member

danquirk commented Mar 12, 2015

I love this functional stuff but I will be honest and say this isn't likely to happen unless JS ends up adding support for it (in which case I could imagine some popular libraries leveraging this).

What would you expect the emit to be for functions that are named like this but which aren't valid function names in JS?

@danquirk danquirk added the Suggestion label Mar 12, 2015

@basarat

This comment has been minimized.

Copy link
Contributor

basarat commented Mar 13, 2015

Worth mentioning: ES Future might have operator overloading

@frankabbruzzese

This comment has been minimized.

Copy link

frankabbruzzese commented Jun 1, 2016

Since all threads about operators oveloading were closed as duplicate of this thread, I suppose this is the right place to speak of it.

I understand, the close javascript <-> typescript code argument, so I understand that it would be awful to have in javascript "strange names" that translate typescript overloaded operators.

However:

  1. as mentioned by @basarat ES7 might have operator overloading
  2. One might define a normal method with the option of using an operator instead of the method name. ie operator might be associated to a method as a kind of syntactic sugar.

I am implementing a C# style IQueryable<T>,and having operator overloading would make posssible to have exactly the same C# syntax. Thanks to methods and operator overloading then I might organize in such a way that function of the type m => m.Name == "Peter" || m.Surname == "Peter" passed to where clauses once executed on a special object(instead of T) would produce the syntactic tree of the expression(which is what I need) instead of the "usual" result. Without operator overloading I'll be forced to write awful stuffs like:

m=> m.Name.eq("Peter")
        .or(m.Surname.eq("Peter"))
@DanielRosenwasser

This comment has been minimized.

Copy link
Member

DanielRosenwasser commented Jun 1, 2016

as mentioned by @basarat ES7 might have operator overloading

Just as a heads up @frankabbruzzese, it didn't. ES2016 was finalized with a new exponentiation operator (**) but that's about it. I don't think we could do this without an advanced-stage proposal on ECMAScript itself.

@kitsonk

This comment has been minimized.

Copy link
Contributor

kitsonk commented Jun 2, 2016

And there is no proposal, stage 0 or otherwise regarding this. I just looked. I can't even find an inactive proposal.

@frankabbruzzese

This comment has been minimized.

Copy link

frankabbruzzese commented Jun 2, 2016

I was thinking just to a syntactic sugar for existing methods. Something like:

class Person{
    name: string;
    Id: number;
    constructor(theName: string, theId: number) { this.name = theName;  this.Id=theId;}
    Equal(other: Person): '=='  {
        Id == other.Id;
    }
}

We define a standard method, like Equal above and then provide an operator to use in place of the method, as a syntactic sugar. When code is transpiled operator disappears and is substituted by the original method. Javascript code remains human readable and close to the original typescript.

@kitsonk

This comment has been minimized.

Copy link
Contributor

kitsonk commented Jun 2, 2016

The way ECMAScript will likely solve this is through well known Symbols. It would be better to find a solution that embraces that... For iterators that is already a feature of the language by adding a Symbol.iterator to an object/class. The best possible solution for your desires is to champion ECMAScript to recognise additional well known symbols and then everyone benefits.

@frankabbruzzese

This comment has been minimized.

Copy link

frankabbruzzese commented Jun 2, 2016

@kitsonk not clear how symbols myght help me. The only way a well known symbols might help me is a property indexed by a symbol that returns the syntactic tree of a function...that is what I need (without being forced to load a whole JavaScript compiler written in Javascript). However, not sure actual implementations stores syntactic trees in functions, so that they might accomodate with an acceptable effort this feature.
Anyway substituting a.Equal(b) with a == b ...and symilar might simplify a lot several JavaScript fluent interfaces

@kitsonk

This comment has been minimized.

Copy link
Contributor

kitsonk commented Jun 2, 2016

The "vision" of Symbols is that they will allow modification of run-time behaviour. For example, if ECMAScript agreed on Symbol.looseEquals for example, you could do this:

class Person{
    name: string;
    Id: number;
    constructor(theName: string, theId: number) { this.name = theName;  this.Id=theId;}
    [Symbol.looseEquals](other: Person): boolean {
        this.Id == other.Id;
    }
}

const personA = new Person();
const personB = new Person();

console.log(personA == personB);

It is use cases like this that Symbols were intended for. Like with the Symbol.toPrimitive and Symbol.toStringTag which deal with coercion of objects by the run-time engine.

TypeScript taking this on, as described though, is an anti-pattern for TypeScript. Design goal number 8:

Avoid adding expression-level syntax.

So even if there was a proposal that the TypeScript team would consider, it should align to the way these things are meant to be accomplished in ECMAScript, not introduce new constructs.

In fact there is nothing preventing you know from creating a Symbol and using it internally and doing something similar already:

Symbol.looseEquals = Symbol('looseEquals');

class Person {
    name: string;
    Id: number;
    constructor(theName: string, theId: number) { this.name = theName;  this.Id=theId;}
    [Symbol.looseEquals](other: Person): boolean {
        this.Id == other.Id;
    }
}

function looseEquals(a: any, b: any): boolean {
  if (a[Symbol.looseEquals]) {
    return a[Symbol.looseEquals](b);
  }
  return a == b;
}

const personA = new Person();
const personB = new Person();

console.log(looseEquals(personA, personB));
@frankabbruzzese

This comment has been minimized.

Copy link

frankabbruzzese commented Jun 2, 2016

@kitsonk ,
Ok if a symbol for each operator would be inserted in a future standard I would have the problem solved. However, actually, may be I will be dead for the time an acceptable browser support would be available. We are not speaking of just looseEqual but of all comparison and boolean operators...
In the meantime TypeScript can't simulate this with transcompilation, so I remain with your final example.
However, your last looseEqualsexample don't get ready of parentheses and define a function in global space. An actual implementation whould use modules/amespaces so it should be something like myNamespace.looseEquals of better myNamespace.eq.

I am writing an IQueryable library, that developers may use with not trivial filters or selections like:

context.Persons.where(m => (m.Name.startsWith(x) || m.surName == x) && date >= d)......

If I substitute each ==, && and || with ns.eq, ns.or, and ns.and...+ needed parentheses expression would become very hard to read.

Here the point is that operator syntax makes expressions easier to read and understand, that's why expressions are used just with basic types in javascript (complex types cant use operators), that's why most of libraries prefer using long fluent interfaces to the use of expressions that would imply nested parentheses almost impossible to read and undersdtand.

@kitsonk

This comment has been minimized.

Copy link
Contributor

kitsonk commented Jun 2, 2016

This is boiling down to "I want it, I want it" irrespective of the feedback that TypeScript is highly unlikely to introduce a feature for the reasons stated above. Yet you continue to say "but I want it" and feel restating it will somehow overcome the reasons stated above of why it is unlikely to be introduced. TypeScript is not a separate language, it is intended to be a superset of ECMAScript. Dart is an example of a separate language.

@frankabbruzzese

This comment has been minimized.

Copy link

frankabbruzzese commented Jun 3, 2016

@kitsonk , sorry if my last post sounded like a kind of protest. It wasn't my intention. It was not with you, or with your team, but just sayng to myself: "ok stop on this path and accept Javascript limitations: let use a fluent interface instead of expressions".
In fact while I appreciate your time and your suggestions I don't think there is a solution or a kind of acceptable compromise on this subject. The only way is to renounce to the usage of complex expressions.
I understand your reasons: moving away from javascript standard might cause the "death" of TypeScript in a short time. TypeScript success as compared with other transpilation based frameworks is due just to this, That is why I am thinking just there is no solution! I could have moved toward another framework...but then my software would havel been thrown away by the next wave of JavaScript changes. So I prefer accepting JavaScript limitation and use a fluent interface.
Also looking for kinda compromise makes no sense. If most of advanced object-based languages implemeted operator overloading is because there is no other way to simplify expressions. The point here is that JavaScript is not exactly an object based framework: as a matter of fact there is no run-time support for types...just prototypes.
Also pushing toward the implementation of new well known symbols wouldn't help. Operator overloading has some overlap with "standard behavior customization", but it is quite different and its purpose is different. In fact operator overloading allows the usage of an operator also in types that natively do not support that operator.So, for instance, Symbols might provide overloaded comparison operator just for types that natively admits a comparison, all other types would be coerced to basic types in any a case instead of using a different definition of them.

I analyzed also true "operator overloading proposals", but strangely enough they were publicly introduced in talks of gusys from Google and Mozilla, but never submitted??? Very strange!
I'll try to ask them why, and if they think a similar proposal will be submitted and pushed.

Again thanks for your time.

@kitsonk

This comment has been minimized.

Copy link
Contributor

kitsonk commented Jun 3, 2016

or with your team

Just for clarity, the TypeScript team isn't "my team"... I am just a heavy user of TypeScript, maintainer of a major JavaScript framework of which the next version is TypeScript based.

Also, I am stating that if ECMAScript ever supports operator overloading it will be done via Symbols, as that is the intent of Symbols in the first place. TC39 has made that path clear. From bitter experience, I know the TypeScript team will not invent new solutions for things that are likely to become standards in the future. I respect their decision to hold to their design goals, although it has complicated things. They of course hold to that due to bitter experience related to the ES Module Syntax and the "cost" of not keeping TypeScript aligned. I think it has less to do with the "death" of TypeScript as much as TypeScript is in it for the long haul, with maybe a vision that TypeScript has the potential to become ECMAScript. The only way to do that is ensure that TypeScript is always a superset of ECMAScript, something that I believe is why TypeScript has had success where as items like Dart has faded.

We, in the web, have all paid the cost of isolation and obsolescence due to not getting wide agreement before implementation. TypeScripts "domain" of authority, if you will, is around types and how those are handled. Maybe a bit around meta data and reflection. It also led the charge in decorators, but under a flag. Operator overloading is clearly not something that is core to the vision of TypeScript, no matter how useful it is, therefore, it is highly unlikely the team will suddenly decide that new expression level syntax for operator overloading would be the right thing to do, especially when the proposed constructs here wouldn't ever fit into ECMAScript.

@frankabbruzzese

This comment has been minimized.

Copy link

frankabbruzzese commented Jun 3, 2016

@kitsonk,
I don't think that symbols will ever solve. >, >=, <=, and <, are defined just for basic types. Any attempt to use them with other types corces that types to basic types (string). Thus, defining a well known Symbol for them would only work for basic types. You would never use, say <, with a generic object types (though you might customizethe way it is coerced to a string). The same is true for arithetic operators that corce to basict types. ||, &&, ==, !=, ! instead might be overloaded on all types

About TypeScript, I think, they NEED to be quite "strict" on implementations. In the past they already refused pull requests implementing operator overloading.
I tried to remove some of the reasons they refuse it by restating it as a syntactic sugar on already existing methods...but I understand your point: if, as a rule, they would accept similar proposals, they would eventually move quite far from ECMA standard, thus transforming TypeScript just in one of the thousands of JavaScrip frameworks

@mcdirmid

This comment has been minimized.

Copy link

mcdirmid commented Sep 19, 2016

@frankabbruzzese, which JavaScript frameworks provide static typing and operator overloading? I'm using TS now for heavy duty canvas work and the lack of operators for even vectors is a show stopper. I guess TS's goal of JavaScript purity is not my own, and if there is a more appropriate framework focused instead more on programmer productivity, I would love to use it instead.

@twop

This comment has been minimized.

Copy link

twop commented Sep 21, 2016

I'm also using canvas. So while using TypeScript was an amazing experience(I'm a C# dev) the lack of operators makes the math around Rectangles, Points, Sizes impossible to read and maintain.

Would love to see them.

@frankabbruzzese

This comment has been minimized.

Copy link

frankabbruzzese commented Oct 14, 2016

@mcdirmid , Google DART supports operatos overloading. However, Google choice for Angular2 favourite framework was TypeScript, not DART, since Angular2 itself was wriiten in TypeScript. So it appears that the language the more likely to survive the next few years is TypeScript. Other languages might de facto be abandoned in a a few years, Thus, notwithstanding the absence of operator overloading I decided to go on with TypeScript.

@narfanar

This comment has been minimized.

Copy link

narfanar commented Nov 13, 2016

In one of the closed issues someone raised the point of rewriting operators to functions in the emitted JS (being a type-driven source emitting) going against the close correspondence that TS wants to maintain.

How about allowing operator functions only as 'aliases'. That is, the functionality has to be written in a normal function first, then an alias is created to forward to that function. The emitted JS uses the main function's name throughout.

Basically just syntax for plain assignment (assigning the main function name to the operator) that requires the assigned value to be a function of 1 or 2 arguments.

Something like:

function _myadd(x: Student, y: Student): number {
    // blah
    return blah;
}
function '+' = _myadd;
@gdefraiteur

This comment has been minimized.

Copy link

gdefraiteur commented Jun 11, 2017

please just find a way to add operator overloading to it. :'(

each second without operator overloading in typescript, a baby cat is killed in uganda... :'(

@yahiko00

This comment has been minimized.

Copy link

yahiko00 commented Jul 16, 2017

It would be nice to add such a feature with the experimental status to give the TypeScript team the freedom to break-change operator overloading when ES decides to include it in the official specifications.
With Symbol or as an alias of an existing function, whatever, but we really need it now to make our code readable and maintainable when dealing with vectors! :)

@narfanar

This comment has been minimized.

@graingert

This comment has been minimized.

Copy link

graingert commented Sep 29, 2017

what about using backticks in the function definition and call:

function `+`<a>(left: a[], right: a[]) : a[] {
   return left.concat(right);
}

[1, 2, 3] `+` [4, 5]; // [1, 2, 3, 4, 5]
@RyanCavanaugh

This comment has been minimized.

Copy link
Member

RyanCavanaugh commented Oct 3, 2017

We won't be doing this unless a proposal goes through the ECMAScript process

@graingert

This comment has been minimized.

Copy link

graingert commented Oct 4, 2017

@RyanCavanaugh how does one go about doing that?

@RyanCavanaugh

This comment has been minimized.

@Microsoft Microsoft locked and limited conversation to collaborators Jun 25, 2018

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.