Generic functions?

601 views
Skip to first unread message

Justin Fagnani

unread,
Jun 14, 2012, 11:26:40 AM6/14/12
to General Dart Discussion
Why doesn't Dart have generic functions and methods?

It seems pretty necessary to properly define functions like map() and reduce(), and as Kasper pointed out to me on G+, the lack of generic methods is one reason why Dart needs the 'new' keyword, so that constructors can be an exception to there being no generic methods.

-Justin

Ladislav Thon

unread,
Jun 14, 2012, 11:30:27 AM6/14/12
to Justin Fagnani, General Dart Discussion
Why doesn't Dart have generic functions and methods?

Sean Eagan

unread,
Jun 14, 2012, 1:47:12 PM6/14/12
to Ladislav Thon, Justin Fagnani, General Dart Discussion
Another place where generic methods would be nice IMO is dart:unittest:

void expect<T>(T item, Matcher<T>);

Matcher<num> closeTo(num value, num delta);
Matcher<Map<K, V>> containsPair<K, V>(K key, V value);
Matcher<Map<Dynamic, V>> containsValue<V>(V value);
Matcher<Map<K, Dynamic>> containsKey<K>(K key);
Matcher<String> startsWith(String str);
Matcher<String> endsWith(String str);
Matcher<String> matches(Pattern pattern);
Matcher<String> equalsIgnoringCase(String str);
Matcher<String> equalsIgnoringWhiteSpace(String str);
Matcher<Comparable<T>> lessThan<T extends Comparable<T>>(Comparable<T> c);
Matcher<Comparable<T>> greaterThan<T extends Comparable<T>>(Comparable<T> c);
Matcher<Comparable<T>> lessThanOrEqualTo<T extends
Comparable<T>>(Comparable<T> c);
Matcher<T> greaterThanOrEqualTo<T extends Comparable<T>>(T c);
Matcher<Collection<E>> every(Matcher<E>);
Matcher<Collection<E>> some(Matcher<E>);
Matcher<bool> get isTrue();
Matcher<bool> get isFalse();
Matcher<num> get isPositive();
Matcher<num> get isNegative();
Matcher<num> get isNonPositive();
Matcher<num> get isNonNegative();
Matcher<num> get isZero();
Matcher<num> get isNonZero();
Matcher<Function> get throws();
Matcher<Function> get returnsNormally();
Matcher<Future> get completes();

example usage:

expect<bool>(b, isTrue);
expect<num>(i, lessThan<num>(6));
expect<String>(s, matches(new Regexp(@"\d+"));

The single argument form, "expect(x)", could be changed to
"expectTrue(x)" if "expect(x, isTrue)" is too long.
--
Sean Eagan

Sean Eagan

unread,
Jun 14, 2012, 2:41:07 PM6/14/12
to Ladislav Thon, Justin Fagnani, General Dart Discussion
However, generic methods wouldn't be needed to achieve this type
safety if "expect" were instead an instance method on Matcher, in
which case would probably want to rename it to something like "test":

isTrue.test(b);
isPositive.test(x);
matches(new Regexp(@"\d+")).test(s);

...then the type arguments are not needed on each call.

Cheers,
Sean

Bob Nystrom

unread,
Jun 14, 2012, 2:44:34 PM6/14/12
to Sean Eagan, Ladislav Thon, Justin Fagnani, General Dart Discussion
The main problem with generic methods that I've heard from Gilad and Peter is that they beg for type inference. When you invoke a generic method in other languages, you generally don't have to pass the type arguments explicitly. Instead, they are inferred from the types of the value arguments. That means that the spec needs to precisely describe what that inference algorithm is. It's doable, but adds a lot of complexity, especially in a language with a dynamic type, and a type system that is unsound in interesting ways.

In Dart, there's also the open question of what that reified type argument should be at runtime: the statically inferred type, or the dynamically detected one based on the argument's runtime type? Consider:

argIsInt<T>(T obj) => print(123 is T);

main() {
  argIsInt('s'); // false
  argIsInt(123); // true

  var obj = 123;
  argIsInt(obj); // ?

  Object obj2 = 123;
  argIsInt(obj2); // ?
}

If you were to translate that to C#, the last two "?" would both print "false" because the type argument is statically inferred. In Dart, I think we would probably want it to print "true", but that may lead to surprising language complexity.

This isn't to say that generic methods aren't doable, but there's enough potential surprises there that we'd like to avoid worrying about it now. You'll note that the generics that Dart does have don't have this problem. With generic classes and even generic factory constructors, you always pass the type arguments explicitly.

Cheers!

- bob

Justin Fagnani

unread,
Jun 14, 2012, 2:53:29 PM6/14/12
to Bob Nystrom, Sean Eagan, Ladislav Thon, General Dart Discussion
On Thu, Jun 14, 2012 at 11:44 AM, Bob Nystrom <rnys...@google.com> wrote:
The main problem with generic methods that I've heard from Gilad and Peter is that they beg for type inference. When you invoke a generic method in other languages, you generally don't have to pass the type arguments explicitly. Instead, they are inferred from the types of the value arguments. That means that the spec needs to precisely describe what that inference algorithm is. It's doable, but adds a lot of complexity, especially in a language with a dynamic type, and a type system that is unsound in interesting ways.

In Dart, there's also the open question of what that reified type argument should be at runtime: the statically inferred type, or the dynamically detected one based on the argument's runtime type? Consider:

argIsInt<T>(T obj) => print(123 is T);

main() {
  argIsInt('s'); // false
  argIsInt(123); // true

  var obj = 123;
  argIsInt(obj); // ?

  Object obj2 = 123;
  argIsInt(obj2); // ?
}

If you were to translate that to C#, the last two "?" would both print "false" because the type argument is statically inferred. In Dart, I think we would probably want it to print "true", but that may lead to surprising language complexity.

Don't generics in Dart already have this behavior? Consider this:

class Value<T> {
  T value;
  Value(T this.value) {}
  bool isSameType(var o) => (o is T);
}

main() {
  Value<String> s = new Value<String>("hi");
  print(s.isSameType(1)); // false
  print(s.isSameType("bye")); // true
  var x = "x";
  print(s.isSameType(x)); // true
}

-Justin

Christopher Wright

unread,
Jun 14, 2012, 3:08:57 PM6/14/12
to Justin Fagnani, Bob Nystrom, Sean Eagan, Ladislav Thon, General Dart Discussion
On 14 June 2012 18:53, Justin Fagnani <justin...@google.com> wrote:


On Thu, Jun 14, 2012 at 11:44 AM, Bob Nystrom <rnys...@google.com> wrote:
The main problem with generic methods that I've heard from Gilad and Peter is that they beg for type inference. When you invoke a generic method in other languages, you generally don't have to pass the type arguments explicitly. Instead, they are inferred from the types of the value arguments. That means that the spec needs to precisely describe what that inference algorithm is. It's doable, but adds a lot of complexity, especially in a language with a dynamic type, and a type system that is unsound in interesting ways.

In Dart, there's also the open question of what that reified type argument should be at runtime: the statically inferred type, or the dynamically detected one based on the argument's runtime type? Consider:

argIsInt<T>(T obj) => print(123 is T);

main() {
  argIsInt('s'); // false
  argIsInt(123); // true

  var obj = 123;
  argIsInt(obj); // ?

  Object obj2 = 123;
  argIsInt(obj2); // ?
}

If you were to translate that to C#, the last two "?" would both print "false" because the type argument is statically inferred. In Dart, I think we would probably want it to print "true", but that may lead to surprising language complexity.

Don't generics in Dart already have this behavior? Consider this:

Dart generics lack type inference. This problem only comes up with type inference. The question is whether the type would be inferred at runtime, using the actual type of the object, or at compile time, using static type annotations.

Using static type annotations is problematic, since you would naturally tend to write:
var a = 123;
argIsInt(a); // argIsInt<Dynamic>(a) -> false

I think a more Dart-y solution here would be to use weak typing more often, rather than trying to hammer in a complex generic system that is guaranteed to cause grief. Learn to love Dynamic.

Sam McCall

unread,
Jun 14, 2012, 3:09:10 PM6/14/12
to Justin Fagnani, Bob Nystrom, Sean Eagan, Ladislav Thon, General Dart Discussion
Bob's comparing a (fixed) value to a (variable) *inferred* type parameter, you're comparing a (variable) value to a (fixed) *specified* type parameter.

Sean Eagan

unread,
Jun 14, 2012, 4:18:59 PM6/14/12
to Ladislav Thon, Justin Fagnani, General Dart Discussion
I submitted an issue for making dart:unittest's Matcher generic:

http://dartbug.com/3652

Cheers,
Sean

Justin Fagnani

unread,
Jun 14, 2012, 5:02:08 PM6/14/12
to Sam McCall, Bob Nystrom, Sean Eagan, Ladislav Thon, General Dart Discussion
Got it. Makes sense now, though I don't think the behavior Bod described is surprising from a language user POV.

-Justin
Reply all
Reply to author
Forward
0 new messages