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

Support declaration of generic type for methods #254

Closed
alxhub opened this Issue Oct 28, 2011 · 49 comments

Comments

Projects
None yet
@alxhub

alxhub commented Oct 28, 2011

Dart should support generic methods/functions, similar to Java:

class Foo<E> {
  E _item;

  /* ... */

  <T> T transform(T fn(E)) {
    return fn(_item);
  }
}

Currently this is supported only by omitting the type information. Additionally, some core Dart code includes the <T> annotation within comments: /<T>/. In order for proper tool/IDE support, Dart should support explicitly declaring generic types for a method.

@DartBot

This comment has been minimized.

Show comment
Hide comment
@DartBot

DartBot Oct 31, 2011

This comment was originally written by drfibonacci@google.com


Removed Type-Defect label.
Added Type-Enhancement, Area-Language, Triaged labels.

DartBot commented Oct 31, 2011

This comment was originally written by drfibonacci@google.com


Removed Type-Defect label.
Added Type-Enhancement, Area-Language, Triaged labels.

@gbracha

This comment has been minimized.

Show comment
Hide comment
@gbracha

gbracha Nov 3, 2011

Contributor

We've tried to keep things very simple. but this is obviously a possible enahncement.


Set owner to @gbracha.
Added Accepted label.

Contributor

gbracha commented Nov 3, 2011

We've tried to keep things very simple. but this is obviously a possible enahncement.


Set owner to @gbracha.
Added Accepted label.

@anders-sandholm

This comment has been minimized.

Show comment
Hide comment
@anders-sandholm
Member

anders-sandholm commented Apr 30, 2012

Added apr30-triage label.

@anders-sandholm

This comment has been minimized.

Show comment
Hide comment
@anders-sandholm

anders-sandholm May 1, 2012

Member

Removed apr30-triage label.

Member

anders-sandholm commented May 1, 2012

Removed apr30-triage label.

@anders-sandholm

This comment has been minimized.

Show comment
Hide comment
@anders-sandholm

anders-sandholm May 1, 2012

Member

Added triage1 label.

Member

anders-sandholm commented May 1, 2012

Added triage1 label.

@anders-sandholm

This comment has been minimized.

Show comment
Hide comment
@anders-sandholm

anders-sandholm May 2, 2012

Member

Added this to the Later milestone.
Removed triage1 label.

Member

anders-sandholm commented May 2, 2012

Added this to the Later milestone.
Removed triage1 label.

@peter-ahe-google

This comment has been minimized.

Show comment
Hide comment
@peter-ahe-google

peter-ahe-google May 14, 2012

Contributor

I think type inference and generic functions go hand in hand.

Since I would prefer not having type inference in Dart, I would prefer to not have generic functions.

Contributor

peter-ahe-google commented May 14, 2012

I think type inference and generic functions go hand in hand.

Since I would prefer not having type inference in Dart, I would prefer to not have generic functions.

@DartBot

This comment has been minimized.

Show comment
Hide comment
@DartBot

DartBot May 14, 2012

This comment was originally written by vva...@gmail.com


I want to write top-level function with generics.

for example, I create this top-level function.
Expection<T> expect(T obj){
  return new _ExpectionImpl.expect(obj);
}

use this function invalid case. and raise a type mismatch error on IDE.
expect("foobar").toBe(1);
                                 ↑ warning. message "int is not assignable to String"

(google group log) https://groups.google.com/a/dartlang.org/d/topic/misc/6eTB2Vrrwh0/discussion

DartBot commented May 14, 2012

This comment was originally written by vva...@gmail.com


I want to write top-level function with generics.

for example, I create this top-level function.
Expection<T> expect(T obj){
  return new _ExpectionImpl.expect(obj);
}

use this function invalid case. and raise a type mismatch error on IDE.
expect("foobar").toBe(1);
                                 ↑ warning. message "int is not assignable to String"

(google group log) https://groups.google.com/a/dartlang.org/d/topic/misc/6eTB2Vrrwh0/discussion

@DartBot

This comment has been minimized.

Show comment
Hide comment
@DartBot

DartBot May 26, 2012

This comment was originally written by Konstantin.Solo...@gmail.com


This would be especially helpful in Futures API. There are transform methods which would be improved if they had generics.

DartBot commented May 26, 2012

This comment was originally written by Konstantin.Solo...@gmail.com


This would be especially helpful in Futures API. There are transform methods which would be improved if they had generics.

@gbracha

This comment has been minimized.

Show comment
Hide comment
@gbracha

gbracha Jan 22, 2013

Contributor

Issue #7099 has been merged into this issue.

Contributor

gbracha commented Jan 22, 2013

Issue #7099 has been merged into this issue.

@gbracha

This comment has been minimized.

Show comment
Hide comment
@gbracha

gbracha Jun 3, 2013

Contributor

Issue #11033 has been merged into this issue.

Contributor

gbracha commented Jun 3, 2013

Issue #11033 has been merged into this issue.

@justinfagnani

This comment has been minimized.

Show comment
Hide comment
@justinfagnani

justinfagnani Jun 3, 2013

Issue #11033 has been merged into this issue.

justinfagnani commented Jun 3, 2013

Issue #11033 has been merged into this issue.

@lrhn

This comment has been minimized.

Show comment
Hide comment
@lrhn

lrhn Jul 5, 2013

Member

Issue #11689 has been merged into this issue.

Member

lrhn commented Jul 5, 2013

Issue #11689 has been merged into this issue.

@lrhn

This comment has been minimized.

Show comment
Hide comment
@lrhn

lrhn Jul 5, 2013

Member

I could easily live with generic methods without type inference. As long as you don't specify the type, it'll just default to "dynamic" anyway, but it would still improve documentation, and you can use it if you want. It's better than just using dynamic, which is the other option.

 T id<T>(T value) => value;

 int x = id<int>("not an int"); // Static type warning! Checked mode error!

So, can we have it, please?

Member

lrhn commented Jul 5, 2013

I could easily live with generic methods without type inference. As long as you don't specify the type, it'll just default to "dynamic" anyway, but it would still improve documentation, and you can use it if you want. It's better than just using dynamic, which is the other option.

 T id<T>(T value) => value;

 int x = id<int>("not an int"); // Static type warning! Checked mode error!

So, can we have it, please?

@polux

This comment has been minimized.

Show comment
Hide comment
@polux

polux Jul 5, 2013

Member

Same here. It would improve documentation and completion so much... (plus it would help the editor's inference for providing completion even when you don't specify the type explicitly).

Member

polux commented Jul 5, 2013

Same here. It would improve documentation and completion so much... (plus it would help the editor's inference for providing completion even when you don't specify the type explicitly).

@munificent

This comment has been minimized.

Show comment
Hide comment
@munificent

munificent Jul 8, 2013

Member

It seems to be tribal knowledge among the language designers that "specifying type inference is bad". Has anyone spelled out why this is bad? I don't seem to recall ever seeing an explanation here. The trend for other statically-typed OOP languages (Java, C#, Scala, TypeScript, et. al.) is towards (local, bottom-up) type inference, so I'm interested to know the rationale behind our desire to go the other direction.

Member

munificent commented Jul 8, 2013

It seems to be tribal knowledge among the language designers that "specifying type inference is bad". Has anyone spelled out why this is bad? I don't seem to recall ever seeing an explanation here. The trend for other statically-typed OOP languages (Java, C#, Scala, TypeScript, et. al.) is towards (local, bottom-up) type inference, so I'm interested to know the rationale behind our desire to go the other direction.

@polux

This comment has been minimized.

Show comment
Hide comment
@polux

polux Jul 8, 2013

Member

I don't know what the exact motivations are but I can see one reason why it
would be bad: type inference in presence of subtyping is hard (or sometimes
impossible). So inference would have to sometimes bail out (as does javac
with generic methods).

If it bails out by issuing an error message that's not dart-ey. If it
silently bails out and passes dynamic instead, the the meaning of a program
with an is-check would depends on whether type inference succeeds or not.
That sound pretty bad.

Even if there wasn't the bailout issue, using type inference for generics
means you trust type annotations (for performing inference) to give a
meaning to the program (because of is-checks). So that alone violates dart
principle of type annotations not affecting a program's semantics.

Member

polux commented Jul 8, 2013

I don't know what the exact motivations are but I can see one reason why it
would be bad: type inference in presence of subtyping is hard (or sometimes
impossible). So inference would have to sometimes bail out (as does javac
with generic methods).

If it bails out by issuing an error message that's not dart-ey. If it
silently bails out and passes dynamic instead, the the meaning of a program
with an is-check would depends on whether type inference succeeds or not.
That sound pretty bad.

Even if there wasn't the bailout issue, using type inference for generics
means you trust type annotations (for performing inference) to give a
meaning to the program (because of is-checks). So that alone violates dart
principle of type annotations not affecting a program's semantics.

@DartBot

This comment has been minimized.

Show comment
Hide comment
@DartBot

DartBot Jul 9, 2013

This comment was originally written by @simonpai


The motivation to have method scope generic type parameter is exactly the motivation to have class scope generic type. Of course adding a feature will add complexity to a language and its implementation, but the arguments about challenges brought by type inference also applies to class scope generic type as well. If it's really that bad, why doesn't Dart just remove all generics features anyway?

DartBot commented Jul 9, 2013

This comment was originally written by @simonpai


The motivation to have method scope generic type parameter is exactly the motivation to have class scope generic type. Of course adding a feature will add complexity to a language and its implementation, but the arguments about challenges brought by type inference also applies to class scope generic type as well. If it's really that bad, why doesn't Dart just remove all generics features anyway?

@justinfagnani

This comment has been minimized.

Show comment
Hide comment
@justinfagnani

justinfagnani Jul 9, 2013

Simon, the difference between generic constructors and generic methods is that people generally expect that you have to specify the type parameters on a constructor and they don't expect to always have to specify the type parameters on a generic method, thus more pressure a type inference.

justinfagnani commented Jul 9, 2013

Simon, the difference between generic constructors and generic methods is that people generally expect that you have to specify the type parameters on a constructor and they don't expect to always have to specify the type parameters on a generic method, thus more pressure a type inference.

@munificent

This comment has been minimized.

Show comment
Hide comment
@munificent

munificent Sep 26, 2016

Member

Yes, it's in progress.

Member

munificent commented Sep 26, 2016

Yes, it's in progress.

@zoechi

This comment has been minimized.

Show comment
Hide comment
@zoechi

zoechi Sep 27, 2016

Contributor

@jodinathan that's working since quite some time already by adding generic type parameters in comments and is supported by dartanalyzer

dynamic /*=T*/ someFunc/*<T,U>*/(dynamic /*=U*/ p1) {
  return someCalc/*<MyType,T>*/() as dynamic /*=T*/;
}

where dynamic is a type that works without using generic type parameters (runtime) and /*=T*/ is the type the analyzer uses to check for type conflicts instead.

There are some issues here (some might be closed) that discuss more details that should help to figure out how to use it.
There seems to be a way to use it without the comments, perhaps a flag, but I don't know details. Just saw it mentioned in some comments.

Contributor

zoechi commented Sep 27, 2016

@jodinathan that's working since quite some time already by adding generic type parameters in comments and is supported by dartanalyzer

dynamic /*=T*/ someFunc/*<T,U>*/(dynamic /*=U*/ p1) {
  return someCalc/*<MyType,T>*/() as dynamic /*=T*/;
}

where dynamic is a type that works without using generic type parameters (runtime) and /*=T*/ is the type the analyzer uses to check for type conflicts instead.

There are some issues here (some might be closed) that discuss more details that should help to figure out how to use it.
There seems to be a way to use it without the comments, perhaps a flag, but I don't know details. Just saw it mentioned in some comments.

@jodinathan

This comment has been minimized.

Show comment
Hide comment
@jodinathan

jodinathan Sep 27, 2016

@zoechi, thanks for the response, however, it seems a bit too hacky for me.
We are still deciding if we will use dart or not in a big project we are projecting. It is important to know where dart is going, and generics is so basic with modern languages that we really expect dart to have it soon.
We were thrilled when studying dart couple days ago, but now there are some down lows that we didn't expect.

jodinathan commented Sep 27, 2016

@zoechi, thanks for the response, however, it seems a bit too hacky for me.
We are still deciding if we will use dart or not in a big project we are projecting. It is important to know where dart is going, and generics is so basic with modern languages that we really expect dart to have it soon.
We were thrilled when studying dart couple days ago, but now there are some down lows that we didn't expect.

@simonpai

This comment has been minimized.

Show comment
Hide comment
@simonpai

simonpai Sep 27, 2016

@munificent, any information on its schedule? Just being curious.

simonpai commented Sep 27, 2016

@munificent, any information on its schedule? Just being curious.

@zoechi

This comment has been minimized.

Show comment
Hide comment
@zoechi

zoechi Sep 27, 2016

Contributor

@jodinathan I wasn't aware you are new to Dart. In this case I can understand :D
I'm using it since several months (with comment syntax) and it's working great but it's not yet released.

What about discussing your issues in https://groups.google.com/a/dartlang.org/forum/#!forum/misc ?

Contributor

zoechi commented Sep 27, 2016

@jodinathan I wasn't aware you are new to Dart. In this case I can understand :D
I'm using it since several months (with comment syntax) and it's working great but it's not yet released.

What about discussing your issues in https://groups.google.com/a/dartlang.org/forum/#!forum/misc ?

@astashov

This comment has been minimized.

Show comment
Hide comment
@astashov

astashov Sep 27, 2016

Last time I tried (a couple months ago), generic methods worked for me in dart2js, in Atom with Dart plugin (but IDEA with Dart plugin still was giving me errors), analyzer supported them, but DartVM still didn't. So, worked everywhere except in DartVM. I could do things like:

T id<T>(T arg) {
  return arg;
}

To enable it for analyzer, so Atom wouldn't highlight them as syntax errors, add to your .analysis_options file:

analyzer:
  language:
    enableGenericMethods: true

And to use them with dart2js, use it with --generic-method-syntax flag, like: dart2js --generic-method-syntax bin/main.dart

astashov commented Sep 27, 2016

Last time I tried (a couple months ago), generic methods worked for me in dart2js, in Atom with Dart plugin (but IDEA with Dart plugin still was giving me errors), analyzer supported them, but DartVM still didn't. So, worked everywhere except in DartVM. I could do things like:

T id<T>(T arg) {
  return arg;
}

To enable it for analyzer, so Atom wouldn't highlight them as syntax errors, add to your .analysis_options file:

analyzer:
  language:
    enableGenericMethods: true

And to use them with dart2js, use it with --generic-method-syntax flag, like: dart2js --generic-method-syntax bin/main.dart

@eernstg

This comment has been minimized.

Show comment
Hide comment
@eernstg

eernstg Sep 27, 2016

Member

Right, Anton just described the options you need to use, so you can use the regular syntax (not the one using magic comments). As long as you do not run in checked mode, you should also be able to use --generic-method-syntax with the virtual machine dart.

However, you should note that the implementations in dart2js and in dart will give you the ability to parse introduction and usage of method type parameters and passing of actual method type arguments, but the runtime semantics associated with these constructs are incomplete:

In dart2js, actual type arguments are parsed but then ignored (nothing is passed at runtime, bounds are not checked), and usages of formal type parameters in the body of a generic function or method are replaced by dynamic or a malformed type (so if T is a method type parameter then T t = 42 will not fail, even in checked mode, no matter whether T == int; similarly List<T> will be a List<dynamic>; and 42 is T is a runtime error whereas <int>[] is List<T> is true). In dart you get a similar treatment in production mode, but it fails with 'malformed type' in checked mode as soon as you encounter a check that involves a formal type parameter (I suspect this will be changed, because it stops almost all programs using generic functions immediately; but for now you can just use production mode).

This means that you can get the static checks (from the analyzer) and the "erased" semantics, which may or may not suffice for your purposes. In Java, for instance, type arguments are always erased, so it might sound fine at first to have this level of support; but as soon as you do things like return new C<T>(..) where T is a method type argument, Dart will reify T in the new object, so you will be able to detect that you got a C<dynamic>, not a C<T>.

And, as Günter mentioned, it's not yet released. ;-)

Member

eernstg commented Sep 27, 2016

Right, Anton just described the options you need to use, so you can use the regular syntax (not the one using magic comments). As long as you do not run in checked mode, you should also be able to use --generic-method-syntax with the virtual machine dart.

However, you should note that the implementations in dart2js and in dart will give you the ability to parse introduction and usage of method type parameters and passing of actual method type arguments, but the runtime semantics associated with these constructs are incomplete:

In dart2js, actual type arguments are parsed but then ignored (nothing is passed at runtime, bounds are not checked), and usages of formal type parameters in the body of a generic function or method are replaced by dynamic or a malformed type (so if T is a method type parameter then T t = 42 will not fail, even in checked mode, no matter whether T == int; similarly List<T> will be a List<dynamic>; and 42 is T is a runtime error whereas <int>[] is List<T> is true). In dart you get a similar treatment in production mode, but it fails with 'malformed type' in checked mode as soon as you encounter a check that involves a formal type parameter (I suspect this will be changed, because it stops almost all programs using generic functions immediately; but for now you can just use production mode).

This means that you can get the static checks (from the analyzer) and the "erased" semantics, which may or may not suffice for your purposes. In Java, for instance, type arguments are always erased, so it might sound fine at first to have this level of support; but as soon as you do things like return new C<T>(..) where T is a method type argument, Dart will reify T in the new object, so you will be able to detect that you got a C<dynamic>, not a C<T>.

And, as Günter mentioned, it's not yet released. ;-)

@munificent

This comment has been minimized.

Show comment
Hide comment
@munificent

munificent Sep 27, 2016

Member

@munificent, any information on its schedule? Just being curious.

Sorry, no. In general we don't tend to commit to schedules for things since we tend to be pretty fluid about organizing our work and we don't want to set expectations and then annoy people when schedules change.

Member

munificent commented Sep 27, 2016

@munificent, any information on its schedule? Just being curious.

Sorry, no. In general we don't tend to commit to schedules for things since we tend to be pretty fluid about organizing our work and we don't want to set expectations and then annoy people when schedules change.

@sethladd

This comment has been minimized.

Show comment
Hide comment
@sethladd

sethladd Dec 7, 2016

Member

Dart 1.21 now has generic methods. (There's an informal specification linked from https://github.com/dart-lang/sdk/blob/master/CHANGELOG.md)

Should we close this?

Member

sethladd commented Dec 7, 2016

Dart 1.21 now has generic methods. (There's an informal specification linked from https://github.com/dart-lang/sdk/blob/master/CHANGELOG.md)

Should we close this?

@munificent

This comment has been minimized.

Show comment
Hide comment
@munificent

munificent Dec 8, 2016

Member

Let's leave it open until the runtime support is there in dart2js and the VM too.

Member

munificent commented Dec 8, 2016

Let's leave it open until the runtime support is there in dart2js and the VM too.

@sethladd

This comment has been minimized.

Show comment
Hide comment
@sethladd

sethladd Dec 8, 2016

Member

Is there a meta bug that is tracking implementation scope?

What does "runtime support in VM" mean? I thought the VM supports generic methods?

Member

sethladd commented Dec 8, 2016

Is there a meta bug that is tracking implementation scope?

What does "runtime support in VM" mean? I thought the VM supports generic methods?

@jmesserly

This comment has been minimized.

Show comment
Hide comment
@jmesserly

jmesserly Dec 8, 2016

Member

What does "runtime support in VM" mean? I thought the VM supports generic methods?

it means reified types:

class C<T> { get t => T; }
f<T>() => T;
main() {
  print(new C<int>().t); // int
  print(f<int>()); // dynamic, but should be int
}
Member

jmesserly commented Dec 8, 2016

What does "runtime support in VM" mean? I thought the VM supports generic methods?

it means reified types:

class C<T> { get t => T; }
f<T>() => T;
main() {
  print(new C<int>().t); // int
  print(f<int>()); // dynamic, but should be int
}
@munificent

This comment has been minimized.

Show comment
Hide comment
@munificent

munificent Dec 8, 2016

Member

What does "runtime support in VM" mean? I thought the VM supports generic methods?

It parses the syntax but treats all type arguments as dynamic. This does not do anything useful in the VM right now:

isOfType<T>(Object obj) {
  print(obj is T);
}

Is there a meta bug that is tracking implementation scope?

I couldn't find one. I emailed the language team to see what the plan for that is.

Member

munificent commented Dec 8, 2016

What does "runtime support in VM" mean? I thought the VM supports generic methods?

It parses the syntax but treats all type arguments as dynamic. This does not do anything useful in the VM right now:

isOfType<T>(Object obj) {
  print(obj is T);
}

Is there a meta bug that is tracking implementation scope?

I couldn't find one. I emailed the language team to see what the plan for that is.

@sethladd

This comment has been minimized.

Show comment
Hide comment
@sethladd

sethladd Dec 8, 2016

Member

Ah, gotcha. Thanks for the clarification.

Member

sethladd commented Dec 8, 2016

Ah, gotcha. Thanks for the clarification.

@brad-jones

This comment has been minimized.

Show comment
Hide comment
@brad-jones

brad-jones Jan 3, 2017

Any update on this? Is the plan to eventually fully support reified generics on methods?

brad-jones commented Jan 3, 2017

Any update on this? Is the plan to eventually fully support reified generics on methods?

@lrhn

This comment has been minimized.

Show comment
Hide comment
@lrhn

lrhn Jan 3, 2017

Member
Member

lrhn commented Jan 3, 2017

@ScottPierce

This comment has been minimized.

Show comment
Hide comment
@ScottPierce

ScottPierce Jan 3, 2017

@lrhn This is the first I'm hearing of Dart 2. Is there any place I can get some information on it?

ScottPierce commented Jan 3, 2017

@lrhn This is the first I'm hearing of Dart 2. Is there any place I can get some information on it?

@brad-jones

This comment has been minimized.

Show comment
Hide comment
@brad-jones

brad-jones Jan 3, 2017

Maybe @irhn is referring to V1.22 ???

brad-jones commented Jan 3, 2017

Maybe @irhn is referring to V1.22 ???

@lrhn

This comment has been minimized.

Show comment
Hide comment
@lrhn

lrhn Jan 3, 2017

Member
Member

lrhn commented Jan 3, 2017

@jodinathan

This comment has been minimized.

Show comment
Hide comment
@jodinathan

jodinathan Jan 3, 2017

jodinathan commented Jan 3, 2017

@munificent

This comment has been minimized.

Show comment
Hide comment
@munificent

munificent Jan 3, 2017

Member

So is it safe to say that if you want to catch up with Dart 2 it is recommended for you to already use strong mode once you can?

Yes, definitely. :)

Member

munificent commented Jan 3, 2017

So is it safe to say that if you want to catch up with Dart 2 it is recommended for you to already use strong mode once you can?

Yes, definitely. :)

@jodinathan

This comment has been minimized.

Show comment
Hide comment
@jodinathan

jodinathan Mar 30, 2017

any news on this?
generic method with dynamic as the type is not very useful

jodinathan commented Mar 30, 2017

any news on this?
generic method with dynamic as the type is not very useful

@eernstg

This comment has been minimized.

Show comment
Hide comment
@eernstg

eernstg Apr 5, 2017

Member

The limited level of support for generic methods that we call generic method syntax is what we plan to support for Dart 1.x. For full support of generic functions you'd need to switch to strong mode, including the strong mode run-time semantics, or wait for Dart 2.0 (and strong mode is essentially a preview of Dart 2.0).

Note, however, that a language like Java has had generic methods for many years at this level (because type arguments are never reified in Java), and it's not useless:

The static checks that you get with strong mode static analysis (e.g., dartanalyzer --strong ...) will reveal any inconsistencies in your invocations of generic functions/methods, and in the usage of any returned results. However, we can do things like e is T or e is List<T> in Dart, and that won't return the correct result when T is a formal type parameter of an enclosing function/method, unless and until the actual type arguments of invocations get reified. In Java you also wouldn't get the correct result, you'd get an "unchecked cast" warning, and the non-throwing evaluation of (T)e wouldn't provide any guarantees at all about the actual type of e. So in that sense it's "no worse than Java", but, of course, we want fully reified type arguments for Dart. It just won't happen in Dart 1.x.

Edit Aug 29, 2017: Updated link to current version of informal spec of generic method syntax.

Member

eernstg commented Apr 5, 2017

The limited level of support for generic methods that we call generic method syntax is what we plan to support for Dart 1.x. For full support of generic functions you'd need to switch to strong mode, including the strong mode run-time semantics, or wait for Dart 2.0 (and strong mode is essentially a preview of Dart 2.0).

Note, however, that a language like Java has had generic methods for many years at this level (because type arguments are never reified in Java), and it's not useless:

The static checks that you get with strong mode static analysis (e.g., dartanalyzer --strong ...) will reveal any inconsistencies in your invocations of generic functions/methods, and in the usage of any returned results. However, we can do things like e is T or e is List<T> in Dart, and that won't return the correct result when T is a formal type parameter of an enclosing function/method, unless and until the actual type arguments of invocations get reified. In Java you also wouldn't get the correct result, you'd get an "unchecked cast" warning, and the non-throwing evaluation of (T)e wouldn't provide any guarantees at all about the actual type of e. So in that sense it's "no worse than Java", but, of course, we want fully reified type arguments for Dart. It just won't happen in Dart 1.x.

Edit Aug 29, 2017: Updated link to current version of informal spec of generic method syntax.

@jodinathan

This comment has been minimized.

Show comment
Hide comment
@jodinathan

jodinathan Apr 5, 2017

@eernstg, can I have full generics with strong mode?
Because I am using it with "analyzer: strong-mode: true" and T is still dynamic.

About Java...
Personally, I try to compare Dart to more modern language like C#.

jodinathan commented Apr 5, 2017

@eernstg, can I have full generics with strong mode?
Because I am using it with "analyzer: strong-mode: true" and T is still dynamic.

About Java...
Personally, I try to compare Dart to more modern language like C#.

@lrhn

This comment has been minimized.

Show comment
Hide comment
@lrhn

lrhn Apr 5, 2017

Member
Member

lrhn commented Apr 5, 2017

@eernstg

This comment has been minimized.

Show comment
Hide comment
@eernstg

eernstg Apr 5, 2017

Member

Right, and I'll make Lasse's explanation a bit more explicit, because we need to make some distinctions that may not be entirely obvious.

With analyzer: strong-mode: true you'll get the strong mode static analysis; here, a formal type parameter of an enclosing generic function/method is not the same thing as dynamic, it is treated like other type arguments (so it may be known to be a subtype of a given bound, and it may have subtype relationships with other type parameters, etc). Similarly, for actual type arguments at call sites you'd get checks based on the declared bounds of the type parameters. All quite different from dynamic.

DDC will generate code that obeys the strong mode run-time semantics; e.g., it will consider a List<dynamic> to have type List<Object> but not List<int>, and it will consider int foo(Object x); to have type Object Function(int) but not int Function(int), because function type subtyping uses contravariance for parameter types. And it reifies function type parameters.

The situation where you use strong mode static analysis and then run your program using Dart 1.x semantics may seem to be a crazy hybrid that you should never even get close enough to shake a stick at. However, the situation isn't actually so bad.

There are lots of situations where strong mode static analysis will reject a program as erroneous, and Dart 1.x static analysis just emits a warning. Given that Dart 1.x warnings generally mean "this is an error, but we can fix it for you" (so you should never ignore them), this is a matter of workflow and not really language semantics. The function type subtype rules in Dart 1.x are very, very forgiving, and basically the function type subtypes in strong mode are just picking the subset of cases that will actually work. This will allow your strong-mode-checked program to continue to run in some situations where it is passing around "a bad function" (say, because an as expression succeeded, but with strong mode semantics it would have failed), but it shouldn't hurt you when running a "good" program. Finally, dynamic doesn't get to play the role as a bottom type ("the almighty type that can do all things"), and in particular you won't be able, with strong mode dynamic semantics, to pass a List<dynamic> where a List<int> is expected, and if you're actually running with Dart 1.x semantics then this won't be detected (but you may have failures, e.g., failing downcasts, later on when you are using the elements in that list).

When it comes to generic methods/functions, your strong-mode-checked program will use the value dynamic in the cases where static analysis expected the actual type argument to be available, and this may make casts succeed where they should have failed (e as T), and it may insert dynamic into newly created instances (return <T>[]; ), and downcasts may succeed where they should have failed (T x = e;), but otherwise the type parameters actually don't matter at run-time.

The List<dynamic> "will work" for all purposes except for a direct type test (it will return true for myList is List<int> in Dart 1.x), and all the other differences are strongly biased towards errors: As long as your program is "working correctly", you won't see any differences.

With this in mind, we think that it does make sense to run programs in Dart 1.x mode after checking them with strong-mode static analysis.

That said, it will be a relief and an improvement when we get strong mode semantics everywhere, too.

About Java and C#: It isn't necessarily fair to consider reification of type arguments as a modern trait, and erasure as old-fashioned. In the functional community it has been considered as an important property that polymorphic types can be handled in a parametric manner, which basically means that it must be completely impossible for the implementation of any given polymorphic programming abstraction (say, a generic function) to depend on the actual type arguments. In return for this particular restriction, we get theorems for free.

This may indeed be helpful for developers when they are reasoning about their software (e.g., about whether a particular modification preserves the meaning of the program as a whole), but it tends to conflict with another very deep force: Object orientation relies on the ability of objects to report on their type ("I'm a String!", "I'm a BankAccount!"), because this is required in order to allow method invocation to be late-bound (such that we can call the correct method implementation for the given object, which is not known statically).

An obviously correct treatment of type parameters in a strictly parametric setup is to erase them. When they've been checked (at compile-time), we don't need them any more.

Reification of type arguments is not something that was invented along with C#, it was part of BETA already in the 1970'ies (in the shape of virtual patterns, which are actually more powerful than type arguments as we know them today).

People will be quick to claim that Java uses erasure because it was impossible to reconcile reified type arguments with the installed base of Java software, and that may indeed be true, but I also think there is a connection to this ancient war between the "theorems for free" crowd and the "OO" crowd. In C#, they made a better choice at some point, at least as seen from the OO side.

In any case, we will have reified generics in Dart, and you can get the full package with strong mode and DDC, and strong-mode-checks-plus-Dart-1.x-semantics as a meaningful stepping stone.

Hope you can live with that for the limited amount of time where it's needed. ;-)

(About the notation: T Function(S) is the new function type syntax. Functional languages commonly use S -> T for the same thing.)

Member

eernstg commented Apr 5, 2017

Right, and I'll make Lasse's explanation a bit more explicit, because we need to make some distinctions that may not be entirely obvious.

With analyzer: strong-mode: true you'll get the strong mode static analysis; here, a formal type parameter of an enclosing generic function/method is not the same thing as dynamic, it is treated like other type arguments (so it may be known to be a subtype of a given bound, and it may have subtype relationships with other type parameters, etc). Similarly, for actual type arguments at call sites you'd get checks based on the declared bounds of the type parameters. All quite different from dynamic.

DDC will generate code that obeys the strong mode run-time semantics; e.g., it will consider a List<dynamic> to have type List<Object> but not List<int>, and it will consider int foo(Object x); to have type Object Function(int) but not int Function(int), because function type subtyping uses contravariance for parameter types. And it reifies function type parameters.

The situation where you use strong mode static analysis and then run your program using Dart 1.x semantics may seem to be a crazy hybrid that you should never even get close enough to shake a stick at. However, the situation isn't actually so bad.

There are lots of situations where strong mode static analysis will reject a program as erroneous, and Dart 1.x static analysis just emits a warning. Given that Dart 1.x warnings generally mean "this is an error, but we can fix it for you" (so you should never ignore them), this is a matter of workflow and not really language semantics. The function type subtype rules in Dart 1.x are very, very forgiving, and basically the function type subtypes in strong mode are just picking the subset of cases that will actually work. This will allow your strong-mode-checked program to continue to run in some situations where it is passing around "a bad function" (say, because an as expression succeeded, but with strong mode semantics it would have failed), but it shouldn't hurt you when running a "good" program. Finally, dynamic doesn't get to play the role as a bottom type ("the almighty type that can do all things"), and in particular you won't be able, with strong mode dynamic semantics, to pass a List<dynamic> where a List<int> is expected, and if you're actually running with Dart 1.x semantics then this won't be detected (but you may have failures, e.g., failing downcasts, later on when you are using the elements in that list).

When it comes to generic methods/functions, your strong-mode-checked program will use the value dynamic in the cases where static analysis expected the actual type argument to be available, and this may make casts succeed where they should have failed (e as T), and it may insert dynamic into newly created instances (return <T>[]; ), and downcasts may succeed where they should have failed (T x = e;), but otherwise the type parameters actually don't matter at run-time.

The List<dynamic> "will work" for all purposes except for a direct type test (it will return true for myList is List<int> in Dart 1.x), and all the other differences are strongly biased towards errors: As long as your program is "working correctly", you won't see any differences.

With this in mind, we think that it does make sense to run programs in Dart 1.x mode after checking them with strong-mode static analysis.

That said, it will be a relief and an improvement when we get strong mode semantics everywhere, too.

About Java and C#: It isn't necessarily fair to consider reification of type arguments as a modern trait, and erasure as old-fashioned. In the functional community it has been considered as an important property that polymorphic types can be handled in a parametric manner, which basically means that it must be completely impossible for the implementation of any given polymorphic programming abstraction (say, a generic function) to depend on the actual type arguments. In return for this particular restriction, we get theorems for free.

This may indeed be helpful for developers when they are reasoning about their software (e.g., about whether a particular modification preserves the meaning of the program as a whole), but it tends to conflict with another very deep force: Object orientation relies on the ability of objects to report on their type ("I'm a String!", "I'm a BankAccount!"), because this is required in order to allow method invocation to be late-bound (such that we can call the correct method implementation for the given object, which is not known statically).

An obviously correct treatment of type parameters in a strictly parametric setup is to erase them. When they've been checked (at compile-time), we don't need them any more.

Reification of type arguments is not something that was invented along with C#, it was part of BETA already in the 1970'ies (in the shape of virtual patterns, which are actually more powerful than type arguments as we know them today).

People will be quick to claim that Java uses erasure because it was impossible to reconcile reified type arguments with the installed base of Java software, and that may indeed be true, but I also think there is a connection to this ancient war between the "theorems for free" crowd and the "OO" crowd. In C#, they made a better choice at some point, at least as seen from the OO side.

In any case, we will have reified generics in Dart, and you can get the full package with strong mode and DDC, and strong-mode-checks-plus-Dart-1.x-semantics as a meaningful stepping stone.

Hope you can live with that for the limited amount of time where it's needed. ;-)

(About the notation: T Function(S) is the new function type syntax. Functional languages commonly use S -> T for the same thing.)

@zoechi

This comment has been minimized.

Show comment
Hide comment
@zoechi

zoechi Aug 3, 2017

Contributor

Can this be closed?

Contributor

zoechi commented Aug 3, 2017

Can this be closed?

@jmesserly

This comment has been minimized.

Show comment
Hide comment
@jmesserly

jmesserly Aug 3, 2017

Member

Can this be closed?

yup! this was fixed in #27501

Member

jmesserly commented Aug 3, 2017

Can this be closed?

yup! this was fixed in #27501

@jmesserly jmesserly closed this Aug 3, 2017

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