function declaration syntax vs. function type syntax

81 views
Skip to first unread message

Ladislav Thon

unread,
Jan 26, 2012, 5:18:59 PM1/26/12
to General Dart Discussion
In the spirit of "everything can change", I'd like to suggest changing function type syntax to be different from function declaration syntax. I'll try to illustrate it on predicates (single-argument functions returning bools), but it is a general problem. A small demonstration program is here: https://github.com/Ladicek/dart-playground/blob/master/function-types.dart

Declaring a function is simple:

bool predicate(obj) => ...

Easy. You can declare a function-typed parameter of a function in the very same way:

void function(something, bool predicate(obj)) => ...

This already puzzles me a little: parameter declaration in Dart has the form of Type name, i.e. the type goes _before_ the name, but in case of function-typed parameters, part of the type actually goes _after_ the name. I already caught myself several times thinking about the right syntax when I was writing such thing.

But here comes the treason: what if you want to declare a function-typed variable? Intuition might lead to

bool predicate(obj);

but that's wrong. That is actually a function declaration, but without a body, and this exact syntax already occurs in interfaces. You can either declare the variable as var, omitting the type entirely, or declare it using a typedef:

typedef bool Predicate(obj);
Predicate predicate;

This situation, if I understand correctly, is the very reason for typedefs in Dart. So, by defining another syntax for function types, distinct from function declarations, you would not only gain some more readability, but you could also get rid of typedefs. Not that I'm suggesting to do it, typedefs have their use, but they shouldn't be mandatory for such simple thing.

Anyway: how about using something like (param1, param2, ..., paramN -> returnType) for function type? The examples above would look like this:

// predicate as a parameter to function
void function(something, (obj -> bool) predicate) => ...

// predicate as a variable
(obj -> bool) predicate;

// and with typed parameters:
void function(something, (Object obj -> bool) predicate) => ...
(Object obj -> bool) predicate;

// and a typedef
typedef (obj -> bool) Predicate;

Here are some edge cases:

// function without arguments returning bool
( -> bool) var1;

// function with single (bool) argument and without defined return type
(arg -> ) var2;
(bool arg -> ) var3;

// function without arguments and without defined return type
( -> ) var4;

These are not particularly nice, but they are quite regular and predictable.

I didn't inspect the grammar, so I'm not sure if this syntax would lead to ambiguities, but I suspect that they could be solved. Also, I guess the fat arrow could be used instead of the thin one, I just used it for better distinguishability.

But as far as I'm concerned, function type syntax isn't the most important thing here, it's the fact that function declaration syntax and function type syntax should be _different_.

Any thoughts? Does this sound reasonable to you? Should I file an issue?

LT

Rémi Forax

unread,
Jan 26, 2012, 5:29:26 PM1/26/12
to mi...@dartlang.org

The main issue is that its hard to parse (int -> int -> int)

>
> LT

R�mi

Dominic Hamon

unread,
Jan 26, 2012, 5:30:07 PM1/26/12
to Ladislav Thon, General Dart Discussion
On Thu, Jan 26, 2012 at 2:18 PM, Ladislav Thon <lad...@gmail.com> wrote:
In the spirit of "everything can change", I'd like to suggest changing function type syntax to be different from function declaration syntax. I'll try to illustrate it on predicates (single-argument functions returning bools), but it is a general problem. A small demonstration program is here: https://github.com/Ladicek/dart-playground/blob/master/function-types.dart

Declaring a function is simple:

bool predicate(obj) => ...

Easy. You can declare a function-typed parameter of a function in the very same way:

void function(something, bool predicate(obj)) => ...

This already puzzles me a little: parameter declaration in Dart has the form of Type name, i.e. the type goes _before_ the name, but in case of function-typed parameters, part of the type actually goes _after_ the name. I already caught myself several times thinking about the right syntax when I was writing such thing.

But here comes the treason: what if you want to declare a function-typed variable? Intuition might lead to

bool predicate(obj);

but that's wrong. That is actually a function declaration, but without a body, and this exact syntax already occurs in interfaces. You can either declare the variable as var, omitting the type entirely, or declare it using a typedef:

typedef bool Predicate(obj);
Predicate predicate;

This situation, if I understand correctly, is the very reason for typedefs in Dart. So, by defining another syntax for function types, distinct from function declarations, you would not only gain some more readability, but you could also get rid of typedefs. Not that I'm suggesting to do it, typedefs have their use, but they shouldn't be mandatory for such simple thing.

Anyway: how about using something like (param1, param2, ..., paramN -> returnType) for function type? The examples above would look like this:

// predicate as a parameter to function
void function(something, (obj -> bool) predicate) => ...

// predicate as a variable
(obj -> bool) predicate;

// and with typed parameters:
void function(something, (Object obj -> bool) predicate) => ...
(Object obj -> bool) predicate;

// and with multiple parameters:
void function(something, (Object a, Object b -> bool) predicate) => >..

I want to put a parenthesis after b so badly.
 

// and a typedef
typedef (obj -> bool) Predicate;

Here are some edge cases:

// function without arguments returning bool
( -> bool) var1;

(void -> bool) var1;
 

// function with single (bool) argument and without defined return type
(arg -> ) var2;
(bool arg -> ) var3;

(arg -> void) var2;
(bool arg -> void) var3;
 

// function without arguments and without defined return type
( -> ) var4;

(void -> void) var4;
 

These are not particularly nice, but they are quite regular and predictable.

I didn't inspect the grammar, so I'm not sure if this syntax would lead to ambiguities, but I suspect that they could be solved. Also, I guess the fat arrow could be used instead of the thin one, I just used it for better distinguishability.

But as far as I'm concerned, function type syntax isn't the most important thing here, it's the fact that function declaration syntax and function type syntax should be _different_.

Why not use this for function declaration as well?

(obj -> bool) predicate => obj != null;

(List<int> list -> bool) isSorted => list[0] < list[1];

Ok, that's getting a little harder to read...
 

Any thoughts? Does this sound reasonable to you? Should I file an issue?

I think it's interesting, but I'm not sure that it's worth the cognitive load of parsing it compared to what's already there.
 

LT

Ladislav Thon

unread,
Jan 26, 2012, 5:32:49 PM1/26/12
to Rémi Forax, mi...@dartlang.org
The main issue is that its hard to parse (int -> int -> int)

That doesn't have to necessarily be valid. Function type would always have to be in parens, so either ((int -> int) -> int) or (int -> (int -> int)).

Dart isn't Haskell :-)

LT

Ladislav Thon

unread,
Jan 26, 2012, 5:41:02 PM1/26/12
to Dominic Hamon, General Dart Discussion
// and with multiple parameters:
void function(something, (Object a, Object b -> bool) predicate) => >..

I want to put a parenthesis after b so badly.

Interesting point, that wouldn't come to my mind. Syntax taste varies sooo much...
 
(void -> bool) var1;

Possible, but overloads the meaning of void too much to my taste.
 
(arg -> void) var2;
(bool arg -> void) var3;

Might work, but AFAIK void someFun(...) {...} and someFun(...) {...} don't have the same meaning in Dart, so...
 
 Why not use this for function declaration as well?

Very unusual syntax, that is never going to happen. And it would destroy the very purpose -- allow the declaration of function-typed variables with the same syntax as function-typed parameters.

LT 

Dirk Detering

unread,
Jan 27, 2012, 12:56:49 AM1/27/12
to Ladislav Thon, General Dart Discussion, Dominic Hamon

Nice this comes up again. We talked about that in another thread once, where I mentioned the discrepancy between the declaration an the error message that indeed uses the single-arrow syntax for reporting.

And once again the mentioned examples show how badly fits c-style leading type into that concept, most of all when Dart prefers to not only give types but also parameter *names* in HOF parameter:

int outer(int param, int ofun(String somepar, String otherpar))
Vs.
outer(param:int, ofun(somepar:String, otherpar:String):int):int

Vs.
outer(param:int, ofun:(String,String)->int)

I consider function declaration syntax for parameter typing weird.

Ladislav Thon

unread,
Jan 27, 2012, 2:06:04 AM1/27/12
to Dirk Detering, General Dart Discussion, Dominic Hamon

And once again the mentioned examples show how badly fits c-style leading type into that concept, most of all when Dart prefers to not only give types but also parameter *names* in HOF parameter


It came to my mind when I was writing this. I actually considered suggesting colon instead of an arrow :-), like in void function(something, (obj1, obj2 : bool) predicate), but I didn't quite like it, especially in edge cases.

But, this:

outer(param:int, ofun(somepar:String, otherpar:String):int):int


should definitely read outer(param: int, ofun: (somepar: String, otherpar: String -> int)): int, if you ask me.

Type after the name isn't a great win either, all the colons are too distractive, and especially function types can easily get hard to read. See also http://blog.jetbrains.com/kotlin/2012/01/the-great-syntactic-shift/ and especially http://blog.jetbrains.com/kotlin/2012/01/the-great-syntactic-shift/#comment-238.

LT

Dirk Detering

unread,
Jan 27, 2012, 6:27:43 AM1/27/12
to Ladislav Thon, General Dart Discussion, Dominic Hamon


Am 27.01.2012 08:06 schrieb "Ladislav Thon" <lad...@gmail.com>:
> But, this:
>
>> outer(param:int, ofun(somepar:String, otherpar:String):int):int
>
>
> should definitely read outer(param: int, ofun: (somepar: String, otherpar: String -> int)): int, if you ask me.

Indeed. My example which you quoted was a first derivation from the current state.  Like the current state it used function declaration as a parameter declaration, only with trailing type syntax.
It was my next step that changed then to typed arrow syntax as parameter type.
What you present is the next step: putting in names again.

But now we have a problem: for the definition of outer you used : 
Fname ( parm:parmtype ) : resulttype

For the parameter you used 
Pname:(parm:parmtype)->resulttype

Shouldn't the outer be declared as
outer(.....)->int    too?
(It's only a thought, I would not prefer that, especially with a following => to separate the body)

Another thought comes to mind:  The dartive mix of parameter types and names for higher order params feels discomforting, although I understand the advantage with optional typing.
Perhaps the Haskell way to separate type and declaration is not so wrong here?
What about

outer::(int, (String, String))->int
outer(param, ofun(somepar, otherpar)) => .....

outer(param, ofun(somepar, otherpar)::(int, (String,String))int => ....

Only brainstorming though.

> Type after the name isn't a great win either, all the colons are too distractive, and especially function types can easily get hard to read.

In my eyes they are, especially in a context where type info can be there or not. Now you read
[Type or Name] [Name or nothing] ( [type or name] [name or nothing] )

With trailing type it becomes:
Name [type or nothing] ( name [type or nothing] )

And with colon as a separator it is always clear that you have a type or not.

See:    outer(int param, int ofun (String, String))
Both 'String' are not types but parameter names !!! with type dynamic.

Btw: In my eyes the colon belongs to the type, not the name (you know UML?) 
Though I would always avoid spaces between name and type, your example would become:

  outer(param :int, ofun :(somepar :String, otherpar :String))

Once again: I would avoid the spaces.

I follow Kotlins argumentation. But not Stephen's in all aspects. Beside that, Kotlin doesn't do as a template because it doesn't mix types and names in the fparam declaration.

Ladislav Thon

unread,
Jan 27, 2012, 8:17:04 AM1/27/12
to Dirk Detering, General Dart Discussion, Dominic Hamon

But now we have a problem: for the definition of outer you used : 
Fname ( parm:parmtype ) : resulttype

For the parameter you used 
Pname:(parm:parmtype)->resulttype

Shouldn't the outer be declared as
outer(.....)->int    too?


My main point is: function declaration should be _different_ from function type. I see why everyone tries to unify them, but 1. they are two distinct concepts 2. it disallows declaring function-typed variables.
 

>See also http://blog.jetbrains.com/kotlin/2012/01/the-great-syntactic-shift/ and especially http://blog.jetbrains.com/kotlin/2012/01/the-great-syntactic-shift/#comment-238.

I follow Kotlins argumentation. But not Stephen's in all aspects. Beside that, Kotlin doesn't do as a template because it doesn't mix types and names in the fparam declaration.


Well, it's just a matter of taste, but I stronly agree with everything Stephen said there.

The thing is: I'm not insisting on the syntax I suggested, I'd only like to get function type syntax separated from function declaration syntax.

LT

Gilad Bracha

unread,
Jan 27, 2012, 1:02:50 PM1/27/12
to Ladislav Thon, Dirk Detering, General Dart Discussion, Dominic Hamon
My personal view is that a clean syntax for function types (as opposed to function *values*, i.e., function declarations and expressions) would be attractive. Mainstream syntax makes that difficult. No firm stance on whether we'd do anything about it or when.
--
Cheers, Gilad

Bob Nystrom

unread,
Jan 27, 2012, 1:43:40 PM1/27/12
to Gilad Bracha, Ladislav Thon, Dirk Detering, General Dart Discussion, Dominic Hamon
On Fri, Jan 27, 2012 at 10:02 AM, Gilad Bracha <gbr...@google.com> wrote:
My personal view is that a clean syntax for function types (as opposed to function *values*, i.e., function declarations and expressions) would be attractive.

Me too! typedefs are a hack in C because function pointer type syntax is so awful. It makes me sad that we have to do something similar in Dart (especially when almost all other languages newer than 30-years-old don't have this problem).

Mainstream syntax makes that difficult.

Even C++ pushed against the limits of C-style declaration syntax when they added templates. With Dart, it feels to me like we've pushed it farther than it can comfortably go. I get the familiarity argument, but I still don't like it.

Local variable declarations are the strangest part to me in Dart. Using a type to declare a variable like "int foo" makes sense in C because creating a variable isn't even meaningful without knowing the storage requirements for it, which is determined by the type.

That doesn't apply to Dart in the least. Storage and type are unrelated to each other, especially since the types are completely optional!

- bob

Ladislav Thon

unread,
Jan 29, 2012, 6:03:37 AM1/29/12
to Bob Nystrom, Dirk Detering, Gilad Bracha, General Dart Discussion, Dominic Hamon

Thinking about the syntax some more (I admit that my original suggestion with the arrow is strange at least), what about just changing the current order? Instead of

void function(something, bool predicate(obj)) {...},

what about

void function(something, bool(obj) predicate) {...}?

Variable declaration would look like

bool(obj) predicate;

and a typedef (if we decide to keep them)

typedef bool(obj) Predicate;

Hmm, not sure it would work when the return type is omitted:

void function(something, (obj) predicate) {...}
(obj) predicate;
typedef (obj) Predicate;

That's weird. It's difficult indeed...

LT

Dirk Detering

unread,
Jan 29, 2012, 1:21:34 PM1/29/12
to Ladislav Thon, Gilad Bracha, Bob Nystrom, General Dart Discussion, Dominic Hamon


Am 29.01.2012 12:03 schrieb "Ladislav Thon" <lad...@gmail.com>:
>
> Thinking about the syntax some more (I admit that my original suggestion with the arrow is strange at least), what about just changing the current order? Instead of
>
> void function(something, bool predicate(obj)) {...},
>
> what about
>
> void function(something, bool(obj) predicate) {...}?

I think what we need is a stress list of different constructs each being a showcase for a specific situation. Then we can check each syntax proposal against this list. The list must include:

* function with parameter of type A->B
* same with type A,B,C->D
each of the forementioned with only parameter names (like now in Dart)
* same as before plus parameter types
* function returning a function type
* use a generic type for each type param above

Ladislav, how would the following function read following your proposal, without and with the regarding param types:

(A, B->C) -> (A->C)   ?

This being a typical functional construct, I consider it unreadable under the current typing rules.

Dirk Detering

unread,
Jan 29, 2012, 3:37:24 PM1/29/12
to Ladislav Thon, Gilad Bracha, Bob Nystrom, General Dart Discussion, Dominic Hamon


> (A, B->C) -> (A->C)   ?
>
> This being a typical functional construct, I consider it unreadable under the current typing rules.

D'oh, surely that should have been:
((A,B)->C) -> (A->C)

Ladislav Thon

unread,
Jan 30, 2012, 4:03:25 AM1/30/12
to Dirk Detering, Gilad Bracha, Bob Nystrom, General Dart Discussion, Dominic Hamon

I think what we need is a stress list of different constructs each being a showcase for a specific situation. Then we can check each syntax proposal against this list. The list must include:

* function with parameter of type A->B
* same with type A,B,C->D
each of the forementioned with only parameter names (like now in Dart)
* same as before plus parameter types
* function returning a function type
* use a generic type for each type param above


Dirk, you are asking important questions, but I think you could answer them yourself easily (with some parens in case of functions returning functions). What I think is more important are the edge cases:

1. omitted return type. Dart allows this, if I understand correctly, and assumes that the return type is Dynamic;
2. no parameters;
3. both 1 and 2 together.

In the 3rd case, my second suggestion would lead to () being a function type of a zero-arity function with return type omitted. That sucks badly.

I could only come up with two solutions:

1. Use some punctuation. My first suggestion goes this way and uses the arrow. I think that this is the exact thing that Dart tries to avoid.
2. Make return type in function type mandatory. I don't think that this is going to happen.

So I'm officially declaring defeat.

LT
Reply all
Reply to author
Forward
0 new messages