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

Be able to closurize constructors #10659

Closed
alan-knight opened this issue May 14, 2013 · 32 comments
Closed

Be able to closurize constructors #10659

alan-knight opened this issue May 14, 2013 · 32 comments
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). core-l type-enhancement A request for a change that isn't a bug

Comments

@alan-knight
Copy link
Contributor

alan-knight commented May 14, 2013

It would be nice to be able to closurize constructors. I find myself writing a de-serialization for DateTime that needs to be of the form

if (isUtc) {
  return new Date.utc(<long parameter list...>);
} else {
  return new Date(<identical long parameter list...>);
}

It's not that big a problem, but it would be nice to be able to write the parameter list once. There are some obvious questions. How would you refer to the default constructor? Could you provide type arguments? Is it an issue that invoking a closure might implicitly be doing a "new"?

@gbracha
Copy link
Contributor

gbracha commented May 22, 2013

Set owner to @gbracha.
Added Accepted label.

@munificent
Copy link
Member

How would you refer to the default constructor?

new Foo

The obvious syntax for this to me is to just look like a constructor invocation without any (). That's what closurized method calls look like. So:

  new Foo(1, 2, 3);

would do the same as:

  var closure = new Foo;
  closure(1, 2, 3);

@DartBot
Copy link

DartBot commented Sep 23, 2013

This comment was originally written by @seaneagan


I like it! Only down side is that "new " is redundant for named constructors.

Consider:

new Foo
new Foo.bar

vs

Foo.new
Foo.bar

@DartBot
Copy link

DartBot commented Dec 16, 2013

This comment was originally written by @stevenroose


What's the chance that this will make it into the codebase anytime soon?

I'm working on a library that may benefit from closurizable constructors. So it this change will eventually come through, I would pause a portion of the implementation and focus on another part first. If it doesn't, I'll need to find an alternative solution.

@gbracha
Copy link
Contributor

gbracha commented Dec 16, 2013

It's not likely to be very soon, since we are trying to keep things stable. I believe it will come through, but it may be quite a long time.

@DartBot
Copy link

DartBot commented Dec 17, 2013

This comment was originally written by @stevenroose


Will this change cause instability then? It shouldn't break anything.
Closurizing regular methods and static methods is already possible, how
different is a constructor from a static method?
But I understand it can take a while, I'll be patient :-)

@DartBot
Copy link

DartBot commented Dec 28, 2013

This comment was originally written by @seaneagan


My use case:

seaneagan/unscripted#15

In unscripted, I would like to uniformly use closures to model command-line script behavior. The closures are annotated with command-line metadata, but in the case of sub-commands the methods are usually constructors. So my users would need to create a dummy closurizable factory method which delegates to the constructor and has the metadata. I imagine there are other scenarios where the metadata would actually have to be duplicated on manually created constructor closures, along with the entire constructor signature (including the return type, i.e. the class being constructed), and refactoring tools would be of no help when the constructor changes.

@DartBot
Copy link

DartBot commented Dec 28, 2013

This comment was originally written by @seaneagan


I saw a closurization operator [1] was added to ClassMirror, but it appears to not support constructors, and doesn't specify whether the returned closure's function [2] is the actual method declaration which would need to be the case in order to get to the metadata.

@DartBot
Copy link

DartBot commented Apr 10, 2014

This comment was originally written by @seaneagan


It's probably obvious, but constructor closures should be canonicalized constants, just like any other static method closures. That would be useful for using them as default factory methods, since default values must be constants.

@DartBot
Copy link

DartBot commented May 22, 2014

This comment was originally written by @seaneagan


It would be nice if this could support type arguments:

var fooFactory = new Foo<int>;
var barFactory = new Bar.baz<String>;

@kevmoo
Copy link
Member

kevmoo commented May 22, 2014

Removed Type-Defect label.
Added Type-Enhancement label.

@gbracha
Copy link
Contributor

gbracha commented Aug 26, 2014

Issue #17907 has been merged into this issue.

@gbracha
Copy link
Contributor

gbracha commented Feb 7, 2015

Just a note that we are looking at this again.

@DartBot
Copy link

DartBot commented Feb 7, 2015

This comment was originally written by @stevenroose


Nice to hear that! I have ((x) => new X(x)) code blocks everywhere.

@DartBot
Copy link

DartBot commented Mar 4, 2015

This comment was originally written by @kaendfinger


See @­gbracha's proposal here: https://github.com/gbracha/generalizedTearOffs/blob/master/proposal.md

@alan-knight alan-knight added Type-Enhancement area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). labels Mar 4, 2015
@jiridanek
Copy link

I guess https://github.com/gbracha/metaclasses/blob/master/proposal.md is supposed to lead to another solution (as reference for people finding this issue first).

@zoechi
Copy link
Contributor

zoechi commented Oct 20, 2015

I think this can be closed.

void main() {
  var constructor = new SomeClass#someConstructor;
  var x = constructor();

  print(dateConstructor(isUtc: true)(2000,1,1));
  print(dateConstructor(isUtc: false)(2000,1,1));
}

Function dateConstructor({bool isUtc}) {
  if(isUtc) {
    return new DateTime#utc;
  } else {
    return new DateTime#;
  }
}

class SomeClass {
  SomeClass.someConstructor() {
    print('constructing...');
  }
}

runs fine, just the analyzer still shows warnings for #.

output:

constructing...
2000-01-01 00:00:00.000Z
2000-01-01 00:00:00.000

@kevmoo kevmoo added P2 A bug or feature request we're likely to work on type-enhancement A request for a change that isn't a bug and removed accepted labels Feb 29, 2016
@rrousselGit
Copy link

  var constructor = new SomeClass#someConstructor;
  var x = constructor();

This doesn't work anymore. At least not in the dartpad and on my project.
Any explanation ?

@zoechi
Copy link
Contributor

zoechi commented Dec 26, 2017

@rrousselGit that feature was removed a short time after it was added. They'll try to find a better approach sometimes after Dart 2.0

@natebosch
Copy link
Member

Now that new is optional it changes the way the syntax feels.

Since new is still a keyword we're already not allowed to define a named constructed as new, so ClassName.new tearing off the default constructor shouldn't be breaking, and makes named constructor tearoffs look like static method tearoffs.

@jaumard
Copy link

jaumard commented Dec 2, 2018

Any plan on this now that dart 2.0 is out for some time?

@rrousselGit
Copy link

so ClassName.new tearing off the default constructor shouldn't be breaking, and makes named constructor tearoffs look like static method tearoffs.

Following that logic, default is probably a better option than new

@lrhn
Copy link
Member

lrhn commented Dec 3, 2018

I have a plan! It's still mostly in my head, but it exists.
It requires allowing the id<typeargs> syntax in more places. When we do that, a lot of things become possible:

  • Denoting a constructor: List<int>.generate
  • Denoting a specialized generic function: T id<T>(T x)=> x; var intId = id<int>;
  • Denoting a specialized generic type: Type listOfInt = List<int>;

I hope we can use the context type to figure out whether something like List<int> or Symbol denotes the type or the default constructor. If the target type is a function type or Function, it's the constructor, otherwise it's the Type. That avoids having to add more syntax that isn't actually related to the constructor.

@lrhn lrhn added core-l and removed P2 A bug or feature request we're likely to work on labels Dec 6, 2018
@lrhn lrhn added this to Non-Breaking And Simple in Language Enhancement Categories Dec 14, 2018
@nic-hartley
Copy link

Just want to throw in my $.02:

Is there any particular reason why Date itself can't be used for the default ctor function reference? AFAIK there aren't any situations where there'd be ambiguity between referencing the Date type and Date's default ctor, and it'd mimic the call syntax much more closely, without needing an odd special syntax for it:

if (isUtc) {
  d = Date(...);
} else {
  d = Date.utc(...);
}

// becomes
final ctor = isUtc ? Date : Date.utc;
d = ctor(...);

I'm not at all familiar with how the Dart compiler works internally, but from what I know of compilers in general it's definitely possible to say, "Hey, that typename is being used as an expression, so it has to mean the default constructor".

Am I missing an edge case where there is ambiguity?

@lrhn
Copy link
Member

lrhn commented Jan 4, 2019

It's unlikely that final ctor = isUtc ? Date : Date.utc; can be made to work, mainly because the type system cannot guess what you mean.
The Date expression is evaluated without any type hint, then the result of that is combined with the Date.utc constructor tear-off result, which is likely a function type.
If Date has type Type, then even if it's a callable object, the type of ctor will still be Object.

In Dart 2, class instances are not functions. To make Date useful as both a Type object and function-typed tear-off of the constructor, the same object would have to both be a function and implement Type. That's very confusing to the type system. We might be able to make Date a Type object that is also callable (but I'm not sure what the run-time type of that would be,)

I think it's more promising to let the meaning of Date depend on the context type. Then you would have to write:

Function ctor = isUtc ? Date : Date.utc;`

to give the type hint needed to distinguish Date-as-a-Type from Date-as-a-constructor.

@nic-hartley
Copy link

@lrhn Whoops, I forgot that the type is, itself, an object (i.e. final foo = Date is valid, and gives you the RTTI for Date). Call it a C++ hangover.

@slightfoot
Copy link
Contributor

Over the past year working with Flutter. I've found several times I would like to refer to a constructor function. What was the problem with the old Someclass#someconstructor reference? Is this issue actually scheduled to be worked on?

@zoechi
Copy link
Contributor

zoechi commented Jan 4, 2019

You can always create a closure for that () => Date(), () => Date.utc()

@rrousselGit
Copy link

That's one of the reasons why I think spread operator for classes would be lovely.

() => Foo() is fine, but if it has tons of arguments, then it's not anymore.

@zoechi
Copy link
Contributor

zoechi commented Jan 4, 2019

I assume tear-offs for constructors and getters/setters are still planned.
Closures are only a workaround until it's done.

@nic-hartley
Copy link

I think we should also consider the utility in functions like map as another case. For example, let's say you make a Flutter widget FancyText which renders text... fancily. You have an array of strings to put into a Column so they all render in a nice, er, column. Right now you'd have to write:

Column(
  children: textArray.map((s) => FancyText(s))
)

With this, you could write

Column(
  children: textArray.map(FancyText.new)
)

That's obviously a trivial example, but it is a (very nearly) real one that I recently had to use, and in my experience with other languages -- typically more functional ones, though not exclusively -- it's very common. Letting ctors be called like any other function taking arguments and returning a FancyText would be useful.

@eernstg
Copy link
Member

eernstg commented Sep 3, 2020

In similar situations we've moved an SDK issue to the language repository, but in this case dart-lang/language#216 contains a more recent version of the same proposal, so I'll close this one. Please go there for additional discussion!

@eernstg eernstg closed this as completed Sep 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). core-l type-enhancement A request for a change that isn't a bug
Projects
Language Enhancement Categories
Non-Breaking And Simple
Development

No branches or pull requests