(based on discussion in #376)
Proposing new syntax for tear-offs to cover several use cases. General form: obj.(thing-to-tear-off)
Examples:
a.(+) // operator tear-off
a.(unary-)
a.(get x) // getter tear-off
a.(set x) // setter tear-off
Foo.(new) // default constructor tear-off
Foo.(new named) // named constructor tear-off
Cf. this discussion on dart-misc:
It would also be useful to consider tear-offs of instance methods independently of the receiver. That is, a method with k positional arguments is torn off as a function with k + 1 positional arguments, and the first argument is the receiver.
Getters and setters could be handled as described above, we'd just change a from an expression denoting an instance to a type literal: Class.(thing-to-tear-off-with-receiver-argument).
Yes! The syntax will become even more uniform then! :-)
I don't see myself ever using a.(get x) when I can write () => a.x.
They take the same number of characters, but the closure approach is easier to refactor.
What about:
#a
#+a
#-a
#a.x
#=a.x
#Foo
#Foo.named
?
@rrousselGit : #Foo is already a Symbol
The character doesn't really matter.
We can change it to something else, like ~ or ::
@rrousselGit : this issue has a long history. Please notice that without parentheses, compiler (and/or the user reading the program) has difficulty resolving the ambiguity while parsing ::Foo.named - it can as well be (::Foo).named - that is, extension method 'named` called on Function.
If we need to resort to using parentheses anyway, wouldn't it be better to incorporate them into our syntax right from the start? That's the whole point of current proposal.
@eernstg : the syntax can be expanded also in another direction, e.g. if f is a function, then maybe we can implement currying like this: f.(curry "hello", color: Color.red), or f.(fix "hello", color: Color.red). Not sure.
It would also be useful to consider tear-offs of instance methods independently of the receiver.
Does this have the semantics of picking a concrete implementation statically? Or will it have the semantics of wrapping with a closure and the concrete implementation is still picked at runtime?
I don't think the former would work:
class Foo {
int get _private => 1;
int get public => 2;
void doStuff() {
print(_private + public);
}
}
// Some other library.
class Bar implements Foo {
@override
void doStuff() {}
@override
int get public => 3;
// No override of _private possible
}
void main() {
void Function(Foo) tornOff = Foo::doStuff;
tornOff(Foo()); // OK
tornOff(Bar()); // throws? What would the error be?
}
I think the latter would be fine. Effectively Foo::doStuff is syntactic sugar for (Foo f, <other args>) => f.doStuff(<other args>);
Does this have the semantics of picking a concrete implementation statically?
No, the idea is simply that with abstract class C { S foo(T); } a tear-off like myC.foo yields something that works like (T t) => myC.foo(t), and C.foo yields (C self, T t) => self.foo(t). So that's what you call 'the latter'.
What about a default first parameter name called it
=> a + it // operator tear-off
=> a - it
=> a.x // getter tear-off
=> a.x = it // setter tear-off
=> Foo(it) // default constructor tear-off
=> Foo.named(it) // named constructor tear-off
@kasperpeulen :
In #265, this syntax is proposed for a different purpose. (There's also discussion #712 that suggests replacing it with $0,聽 but it's not relevant here). You would probably agree that "operator tearoff" is not the first idea that comes to mind when one looks at expression => a聽+ it :-)
Right, my point is that I don`t think we really need new tear-off syntax, if we have #265.
There is one thing that a tear-off is capable of doing that we can't emulate with a manual rewrite, namely the special behavior for equality tests (operator ==): Two tear-offs of the same method will test equal iff they were torn off the same receiver (o1.foo == o2.foo iff identical(o1, o2)). This property could be carried over to the new kind of tear-off (where we tear an instance method off of a class, yielding a function that adds an extra positional argument accepting the receiver): They could be equal iff they tear off the same method of the same statically known receiver type. A similar property could be defined for getters and setters and constructors.
Another approach to constructor tear-offs will be: introduce partial function application mechanism (pseudo-method bind, instrinsified by a compiler) that works for functions as well as constructors, e.g.
foo(int x, int y) {...}
Function foo1=foo.bind(0); // returns 1-arg function
bar(int x, {int y}) {...}
Function bar1=bar.bind(y:0); // returns 1-arg function
class A {
A(int x, {int y}) {...};
}
Function a1 = A.bind(); // returns 2-arg function with the same parameters as constructor
Function a2 = A.bind(y: 0); // partial application
This mechanism opens new possibilities for writing Flutter literals- see this example
TL;DR:
container(row([menuIcon(), expanded(title), searchIcon()]))
Isn't this the same functionality of this proposal that was aproved back in 2015?
It is similar, but the generalized tear-offs as described in said proposal are not supported in Dart today (not in the specification, not in the implementation).