Skip to content
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

type parameter in operator overloading #1044

Open
dhaval15 opened this issue Jun 23, 2020 · 8 comments
Open

type parameter in operator overloading #1044

dhaval15 opened this issue Jun 23, 2020 · 8 comments
Labels
request Requests to resolve a particular developer problem

Comments

@dhaval15
Copy link

Dart does not support adding types in operator overloading.

void operator |<T>(T value);

It would be great if it is supported for function currying, piping using extension and operator overloading.

@lrhn lrhn transferred this issue from dart-lang/sdk Jun 23, 2020
@lrhn
Copy link
Member

lrhn commented Jun 23, 2020

The biggest issue with generic operators is that you can't pass them explicitly. It would be like a generic function where you could only use the type argument that was inferred, with no way to override it.

It's not completely untenable. It makes sense to use generics as a kind of existential type predicate.
It's not particularly practical when you only use the type variable once, like in the example given here. It's more convenient when it occurs more than once, like:

extension FutX<T> on Future<T> {
  Future<R> operator>><R>(FutureOr<R> Function(T) handler) => this.then<R>(handler);
}
...
   future >> ((x) => x + 1)  // wohoo

(Getters and setters are even harder, and less useful, to make generic that way because they only have one moving part that you can type).

@lrhn lrhn added the request Requests to resolve a particular developer problem label Jun 23, 2020
@mateusfccp
Copy link
Contributor

Well, it would be even nicer if there was no distinction between operators and functions, but I know there's no viability in Dart.

@lrhn
Copy link
Member

lrhn commented Jun 25, 2020

The distinction is mainly syntactical.

The method operator+ is invoked by a + b, and that's the only way to call it, and there is no way to create a tear-off. Therefore the operator method needs to be callable with exactly one argument, and there is no use in allowing optional parameters or type parameters.
Apart from that, operators are really just normal methods.

If we introduced a way to call operators directly, say a.+(b), then we could allow tearoffs (a.+), optional parameters a.+(b, mod: n) or type parameters a.+<num>(b).
Type parameters is the one thing we could allow without adding a way to pass them explicitly, because the language has a way to pass them implicitly (type inference). In general, I prefer that anything inference does, can also be written explicitly (including instantiated generic method tear-offs - #125).

The real question here is how much of this is actually useful and therefore worth the effort. Allowing optional parameters seems spurious, but generics and a way to do tear-off and explicit invocation could be useful.

There are parsing issues. Since both < and << are declarable operators, x.<<y can get tricky to parse. Probably not impossible, but needing too far a look-ahead to know what you are doing, can easily make the parser more fragile and harder to add other features to.

@munificent
Copy link
Member

I was thinking that extensions might let you accomplish this:

class C {}

extension<T> on C {
  T operator |(T value) {
    print("pipe $T");
    return value;
  }
}

main() {
  var c = C();
  c | true;
  c | 123;
}

But this prints:

pipe dynamic
pipe dynamic

So I guess type inference on extensions doesn't to upwards inference based on the RHS of an operator?

@leafpetersen
Copy link
Member

@munificent the type of the arguments are not taken into account when resolving the generic type parameters of the extension. This was a deliberate choice, based on design discussion at the time. I'm not sure if it works for this use case, but you can change your code to be extension<T> on T ... and T will be resolved based on the receiver type (but not the argument type).

@MelbourneDeveloper
Copy link

I would like to resurrect this one. I am working on various Monads for Dart, and they are quite verbose without operator overloading. It would be nice to have both generic type arguments and the ability to override arbitrary symbols. One issue is that Dart gets too brackety when you call extensions inside extensions and so on.

The main issue is with the equivalent of Haskell's fmap function. It can return an arbitrary type of Monad, but without generic type arguments in operator overloading, it only returns dynamic, which is dangerous.

Will provide examples soon.

@munificent

@dgreensp
Copy link

I just ran into this. I have two types, Bar extends Foo, and I wanted bar + bar to be Bar but bar + foo to be Foo, similar to how int + int is int but int + num is num. I was going to have the return type of + on Bar be the type of its argument.

@lrhn
Copy link
Member

lrhn commented Apr 26, 2024

Indeed, that won't work. You could (maybe) get around it with a generic method like:

abstract class Foo {
  Foo add<T extends Foo>(T other);
}

abstract class Bar extends Foo {
  T add<T extends Foo>(T other);
}

abstract class Baz extends Foo {
  Baz add<T extends Foo>(T other);
}

but without generic operators, you can't do that with +.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
request Requests to resolve a particular developer problem
Projects
None yet
Development

No branches or pull requests

7 participants