Type annotation for Function parameters

1,724 views
Skip to first unread message

Robert Beeger

unread,
Mar 27, 2012, 4:53:25 AM3/27/12
to mi...@dartlang.org
Hi,

Let's say I'm writing a library and have a function that takes a callback as parameter like so:
void someFunction(Function callback) {
  // some code calling the callback
}

Now by looking at the signature of someFunction there is no way to know with what parameters the callback will be called. You can either analyze the implementation of someFunction or if you're lucky I'll provide some documentation that tells you how the callback will be called. It's also not possible to get warned by the compiler that a callback with a wrong list of parameters is passed to someFunction.

Are there any plans to add some kind of parameter information for Function-parameters?

Cheers,
  Robert


Chris Buckett

unread,
Mar 27, 2012, 5:27:09 AM3/27/12
to General Dart Discussion
Hi Robert

You can use the following to indicate the parameters:
http://try.dartlang.org/s/M3Y2

main() {
callback(s,i) {
print(s is String);
print(i is int);
}

someFunction(callback);
}

someFunction(Function callback(String s, int i)) {
callback("Hello", 1);
}




Also, you can define the function type itself using typedef:
http://try.dartlang.org/s/NZ02

typedef MyCallbackFunctionType(String,int);

main() {
MyCallbackFunctionType callback = (s,i) {
print(s is String);
print(i is int);
};

someFunction(callback);
}

someFunction(MyCallbackFunctionType callback) {
callback("Hello", 1);
}

Hope that helps
Cheers,
Chris.

A Matías Quezada

unread,
Mar 27, 2012, 5:27:17 AM3/27/12
to Robert Beeger, mi...@dartlang.org
There are two ways to do this:

Just typing the signature:

void someFunction(void callback()) {
  // some code calling the callback
}

Or with typedef, this will create a function-type, so you can reuse it everywhere

typedef void CallbackType();

void someFunction(CallbackType callback) {
  // some code calling the callback
}

---
A. Matías Quezada


2012/3/27 Robert Beeger <rbe...@gmail.com>

Chris Buckett

unread,
Mar 27, 2012, 5:28:23 AM3/27/12
to General Dart Discussion
The second example has a better link here: http://try.dartlang.org/s/lI02

Robert Beeger

unread,
Mar 27, 2012, 7:11:29 AM3/27/12
to mi...@dartlang.org
Thanks to both of you Chris and A. Matías Quezada.

I have tried it the wrong way around. I had "Function(int) callback" instead of "Function callback(int)".
Now that I see it, it looks logical - somehow. 

Cheers,
  Robert
 

Jay Young

unread,
Mar 27, 2012, 12:38:36 PM3/27/12
to mi...@dartlang.org
Maybe this is a deficiency in Dartboard, but if you pass a string as the second parameter to the callback in your example, it prints "true" and "false" in checked mode instead of throwing an exception:

typedef MyCallbackFunctionType(String,int);

main() {
   MyCallbackFunctionType callback = (s,i) {
    print(s is String);
    print(i is int);
  };

  someFunction(callback);
}

someFunction(MyCallbackFunctionType callback) {
  callback("Hello", 'b');
}

This prints "true" and "false".  Assigning the anonymous function to a variable typed with the typedef isn't enough for checked mode.  You need the types in the closure definition:

typedef MyCallbackFunctionType(String,int);

main() {
   MyCallbackFunctionType callback = (String s,int i) {
    print(s is String);
    print(i is int);
  };

  someFunction(callback);
}

someFunction(MyCallbackFunctionType callback) {
  callback("Hello", 'b');
}

This throws as expected.

Bob Nystrom

unread,
Mar 28, 2012, 2:14:35 PM3/28/12
to Jay Young, mi...@dartlang.org
On Tue, Mar 27, 2012 at 9:38 AM, Jay Young <jayyou...@gmail.com> wrote:
Maybe this is a deficiency in Dartboard

I believe it's doing what the spec says it should do here.
 
, but if you pass a string as the second parameter to the callback in your example, it prints "true" and "false" in checked mode instead of throwing an exception:

typedef MyCallbackFunctionType(String,int);

main() {
   MyCallbackFunctionType callback = (s,i) {
    print(s is String);
    print(i is int);
  };

  someFunction(callback);
}

someFunction(MyCallbackFunctionType callback) {
  callback("Hello", 'b');
}

So there are four type tests that will happen here in checked mode:

1. When you assign (s, i) { ... } to callback, we check that the function type is assignment compatible with MyCallbackFunctionType. In this case, the function's type is (Dynamic, Dynamic) -> Dynamic and MyCallbackFunctionType is (String, int) -> Dynamic. The function subtype rule is the typical one that tests the respective return and parameter types (with covariance for the former and contravariance for the latter). Since Dynamic is both a supertype and a subtype of String and int, those function types are indeed compatible, so there's no warning here.

2. When you call someFunction, we test that the argument type, i.e. the type of the object stored in callback matches the parameter type of someFunction. This is the same (Dynamic, Dynamic) -> Dynamic and (String, int) -> Dynamic test as before, so it passes.

3. When someFunction calls the callback, we check that the argument "Hello" is compatible with the parameter type Dynamic. It is, so no warning.

4. Likewise, when you call the function, we check that the argument "b" is compatible with the parameter type Dynamic. It is, so no warning.

This is a bit surprising because you're expecting that the callback function somehow "remembers" that it was assigned to a variable with a tighter type annotation and would then use that for subsequent type tests, but it doesn't work that way. When Dart is doing a type test in checked mode, it's always looking at the type of the value and not the type annotation of any variable that may happened to have previously held a reference to that object.

Consider:

main() {
  MyCallbackFunctionType callback = (s,i) { ... };
  (true ? callback : null)('Hello', 'b');
}

Here, the function we're invoking is coming from some expression (a ternary operator here, but you can imagine any expression). It would be weird to expect the declared type of the callback variable to somehow flow through that expression up to the call.

Hope that clarifies things a bit. :)

- bob

Peter Ahé

unread,
Mar 29, 2012, 3:32:13 AM3/29/12
to Chris Buckett, General Dart Discussion
On Tue, Mar 27, 2012 at 11:27, Chris Buckett <chrisb...@gmail.com> wrote:
> Hi Robert
>
> You can use the following to indicate the parameters:
> http://try.dartlang.org/s/M3Y2
>
> main() {
>  callback(s,i) {
>    print(s is String);
>    print(i is int);
>  }
>
>  someFunction(callback);
> }
>
> someFunction(Function callback(String s, int i)) {

Did you mean:

someFunction(callback(String s, int i)) {

or

someFunction(void callback(String s, int i)) {

Cheers,
Peter

Peter Ahé

unread,
Mar 29, 2012, 3:40:17 AM3/29/12
to Bob Nystrom, Jay Young, mi...@dartlang.org
On Wed, Mar 28, 2012 at 20:14, Bob Nystrom <rnys...@google.com> wrote:
>
>
> On Tue, Mar 27, 2012 at 9:38 AM, Jay Young <jayyou...@gmail.com> wrote:
>>
>> Maybe this is a deficiency in Dartboard
>
>
> I believe it's doing what the spec says it should do here.
>
>>
>> , but if you pass a string as the second parameter to the callback in your
>> example, it prints "true" and "false" in checked mode instead of throwing an
>> exception:
>>
>> typedef MyCallbackFunctionType(String,int);
>>
>> main() {
>>    MyCallbackFunctionType callback = (s,i) {
>>     print(s is String);
>>     print(i is int);
>>   };
>>
>>   someFunction(callback);
>> }
>>
>> someFunction(MyCallbackFunctionType callback) {
>>   callback("Hello", 'b');
>> }
>
>
> So there are four type tests that will happen here in checked mode:
>
> 1. When you assign (s, i) { ... } to callback, we check that the function
> type is assignment compatible with MyCallbackFunctionType. In this case, the
> function's type is (Dynamic, Dynamic) -> Dynamic and MyCallbackFunctionType
> is (String, int) -> Dynamic. The function subtype rule is the typical one
> that tests the respective return and parameter types (with covariance for
> the former and contravariance for the latter).

Actually, our function subtype rule is not the typical one. See
section "13.5 Function Types" of the specification.

Basically it says that (T) -> S is a subtype of (T') -> S' if T is a
subtype of T' or T' is a subtype of T AND if S is a subtype of S' or
S' is a subtype of S or S' is void.

This is because we want to allow you to do this:

printIt(String f()) { print (f()); }
main() {
Object f() => 'Hello, World!';
printIt(f);
}

Cheers,
Peter

Bob Nystrom

unread,
Mar 29, 2012, 1:33:05 PM3/29/12
to Peter Ahé, Jay Young, mi...@dartlang.org
On Thu, Mar 29, 2012 at 12:40 AM, Peter Ahé <a...@google.com> wrote:

Actually, our function subtype rule is not the typical one. See
section "13.5 Function Types" of the specification.

Basically it says that (T) -> S is a subtype of (T') -> S' if T is a
subtype of T' or T' is a subtype of T AND if S is a subtype of S' or
S' is a subtype of S or S' is void.

Ah, OK. So it has the bidirectional assignment compatibility baked directly into the subtype relation for functions?

I also forgot about the special casing for void.

Thanks for clarifying!

- bob
 

Peter Ahé

unread,
Apr 17, 2012, 11:48:08 AM4/17/12
to Bob Nystrom, Jay Young, mi...@dartlang.org
On Thu, Mar 29, 2012 at 19:33, Bob Nystrom <rnys...@google.com> wrote:
>
>
> On Thu, Mar 29, 2012 at 12:40 AM, Peter Ahé <a...@google.com> wrote:
>>
>>
>> Actually, our function subtype rule is not the typical one. See
>> section "13.5 Function Types" of the specification.
>>
>> Basically it says that (T) -> S is a subtype of (T') -> S' if T is a
>> subtype of T' or T' is a subtype of T AND if S is a subtype of S' or
>> S' is a subtype of S or S' is void.
>
>
> Ah, OK. So it has the bidirectional assignment compatibility baked directly
> into the subtype relation for functions?

Correct. The idea is that it requires a more complicated type system
than we want to be able to precisely express what kind of function you
need. For example, if you have this function:

foo(String x(String key)) { ... }

Here foo says that it needs a function that it will pass a String and
expect a String back. The identity function has this property, but may
be typed like this:

Object identity(Object o) => o;

We want developers spending more time adding real value to their
programs instead of worrying about a fairly complicated type system
that would be required to support this otherwise.

Cheers,
Peter

Reply all
Reply to author
Forward
0 new messages