Optional and named parameter syntax in Dart 2.0

1,163 views
Skip to first unread message

Kasper Peulen

unread,
Aug 18, 2016, 3:32:18 AM8/18/16
to mi...@dartlang.org
Not sure if I recall correctly, but I heard the optional/named parameter syntax is being reconsidered for Dart 2.0.
I haven't see any proposals for that, let me do an informal proposal here:

Postional optional parameters:

Dart 1.0:
String say(String msg, [String from, String device = 'phone']) { ... }

Dart 2.0:
String say(String msg, String from?, String device? = 'phone') { ... }


Named optional parameters:

Dart 1.0:
enableFlags({bool bold, bool hidden: true}) { ... }

Dart 2.0 :
enableFlags({bool bold?, bool hidden? = true}) { ... }


Named parameters:
Dart 1.0:
enableFlags({@required bool bold, @required bool hidden}) { ... }

Dart 2.0 proposal:
enableFlags({bool bold, bool hidden}) { ... }

This seems to me, as the most straight forward way. 

Types on the right/NNBD
With the proposed types on the right side, and non nullable by default, it could look like this:

say(msg: String, from: String?, device: String = 'phone') -> String { ... }

enableFlags({bold: bool?, hidden: bool = true}) { ... }

So now, it is more tricky, because a parameter can be optional and/or nullable. I would say that a parameter is optional if it either has a default parameter or if the type is nullable (or both).

Syntax for named parameters

Now there is one other thing. You may wonder, why do you propose the {...} syntax for named parameters in Dart 2.0? And why a colon `:` instead of the more natural `=` in the call? Well, there is a reason, I want to propose also the following. Named parameters are just sugar.

  enableFlags(bold: true, hidden: false);

This would desugar to writing:

  enableFlags({"bold": true, "hidden": false});

Yes, this is a map. Named parameters are sugar for writing a map. So actually, all parameters are positional now, where the last one can be a Map that can be written with some sugar as named parameters.

So you may wonder, does this really matter anything? Who would write the later? Well, I think it actually does matter, because it helps making it much more natural to write javascript interop, because in javascript devs often add an option object (Map in Dart) as the last parameter. This object just serves as providing a bunch of named/optional parameters. With this proposal, this would map very straight forward to Dart.

Can we generalize this?

If you think about it, this kind of asks to give the Map object to have a more flexible type. Now with this proposal, here:

enableFlags({bool bold, bool hidden}) { ... }

This says now, that the enableFlags function has as parameter a Map that must have two keys, the string bold and the string hidden, both of type bool. So if we can declare the type of the Map so specific here, why not in other Dart code? 

What about this syntax (assuming right hand types in Dart 2.0):

var map: Map<{"bold": bool, "hidden": bool}> = {"bold": true, "hidden": false};

Well this syntax seems quite okay, I think we can provide a very natural sugar for this? Let's look a second at the syntax for named parameters:

enableFlags({bold: bool, hidden: bool}) { ... }
enableFlags(bold: true, hidden: false);

It seems clear to me that the sugar should look like;

var map: {bold: bool, hidden: bool} = (bold: true, hidden: true);

Okay, so we have here a very specific kind of Map object, where all the keys are strings. Of course this is just a plain javascript object. And this should map to a plain javascript object when compiled. This would even give more advantages with javascript interop. For example, you could give much more auto completion and type safety to the react package.

Thoughts?

krupal shah

unread,
Aug 18, 2016, 10:44:03 AM8/18/16
to mi...@dartlang.org

Good examples. There are also inconsistent type names. For example, take String and bool, or List and int. If there is no primitive type in Dart, why some types are written in lower case and others in upper case. In Java, there is a reason for that. and why there are num and int both? Just to maintain compatibility with JS? I would also like that Dart 2.0 remove the ugly ternary expressions and pre-post increment operators. Nobody should write those unreadable stuff. Inconsistancy in a language itself is terrible thing. Go is much cleaner than Dart currently IMHO. Surely, we can hope that Dart 2.0 will have lesser ways of doing same things.

On Aug 18, 2016 1:02 PM, "Kasper Peulen" <kasper...@gmail.com> wrote:
Not sure if I recall correctly, but I heard the optional/named parameter syntax is being reconsidered for Dart 2.0.
I haven't see any proposals for that, let me do an informal proposal here:

Postional optional parameters:

Dart 1.0:
String say(String msg, [String from, String device = 'phone']) { ... }

Dart 2.0:
String say(String msg, String from?, String device? = 'phone') { ... }


Named optional parameters:

Dart 1.0:
enableFlags({bool bold, bool hidden: true}) { ... }

Dart 2.0 :
enableFlags({bool bold?, bool hidden? = true}) { ... }


Named parameters:
Dart 1.0:
enableFlags({@required bool bold, @required bool hidden: true}) { ... }

Dart 2.0 proposal:
enableFlags({bool bold, bool hidden = true}) { ... }

This seems to me, as the most straight forward way. 

Types on the right/NNBD
With the proposed types on the right side, and non nullable by default, you may want to write it like this:

say(msg: String, from: String?, device: String = 'phone') -> String { ... }

enableFlags({bold: bool?, hidden: bool = true}) { ... }

So now, it is more tricky, because a parameter can be optional and/or nullable. I would say that a parameter is optional if it either has a default parameter or if the type is nullable (or both).

Syntax for named parameters

Now there is one other thing. You may wonder, why do you propose the {...} syntax for named parameters in Dart 2.0? And why a colon `:` instead of the more natural `=` in the call? Well, there is a reason, I want to propose also the following. Named parameters are just sugar.

  enableFlags(bold: true, hidden: false);

This would desugar to writing:

  enableFlags({"bold": true, "hidden": false});

Yes, this is a map. Named parameters are sugar for writing a map. So actually, all parameters are positional now, where the last one can be a Map that can be written with some sugar as named parameters.

So you may wonder, does this really matter anything? Who would write the later? Well, I think it actually does matter, because it helps making it much more natural to write javascript interop, because in javascript devs often add an option object (Map in Dart) as the last parameter. This object just serves as providing a bunch of named/optional parameters. With this proposal, this would map very straight forward to Dart.

Can we generalize this?

If you think about it, this kind of asks to give the Map object to have a more flexible type. Now with this proposal, here:

enableFlags({bool bold, bool hidden}) { ... }

This says now, that the enableFlags function has as parameter a Map that must have two keys, the string bold and the string hidden, both of type bool. So if we can declare the type of the Map so specific here, why not in other Dart code? 

What about this syntax (assuming right hand types in Dart 2.0):

var map: Map<{"bold": bool, "hidden": bool}> = {"bold": true, "hidden": false};

Well this syntax seems quite okay, I think we can provide a very natural sugar for this? Let's look a second at the syntax for named parameters:

enableFlags({bold: bool, hidden: bool}) { ... }
enableFlags(bold: true, hidden: false);

It seems clear to me that the sugar should look like;

var map: {bold: bool, hidden: bool} = (bold: true, hidden: true);

Okay, so we have here a very specific kind of Map object, where all the keys are strings. Of course this is just a plain javascript object. And this should map to a plain javascript object when compiled. This would even give more advantages with javascript interop. For example, you could give much more auto completion and type safety to the react package.

Thoughts?

--
For other discussions, see https://groups.google.com/a/dartlang.org/
 
For HOWTO questions, visit http://stackoverflow.com/tags/dart
 
To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.

Bob Nystrom

unread,
Aug 18, 2016, 2:25:17 PM8/18/16
to General Dart Discussion
On Thu, Aug 18, 2016 at 12:32 AM, Kasper Peulen <kasper...@gmail.com> wrote:
Not sure if I recall correctly, but I heard the optional/named parameter syntax is being reconsidered for Dart 2.0.

Yes, although making breaking changes here is obviously really really expensive since it breaks so much code.
 
I haven't see any proposals for that, let me do an informal proposal here:

Postional optional parameters:

Dart 1.0:
String say(String msg, [String from, String device = 'phone']) { ... }

Dart 2.0:
String say(String msg, String from?, String device? = 'phone') { ... }


Named optional parameters:

Dart 1.0:
enableFlags({bool bold, bool hidden: true}) { ... }

Dart 2.0 :
enableFlags({bool bold?, bool hidden? = true}) { ... }


Named parameters:
Dart 1.0:
enableFlags({@required bool bold, @required bool hidden: true}) { ... }

Dart 2.0 proposal:
enableFlags({bool bold, bool hidden = true}) { ... }

This seems to me, as the most straight forward way. 

Using a postfix "?" is pretty neat, but I worry that it's visually very similar to syntax we probably want for nullable types.


Types on the right/NNBD
With the proposed types on the right side, and non nullable by default, you may want to write it like this:

say(msg: String, from: String?, device: String = 'phone') -> String { ... }

enableFlags({bold: bool?, hidden: bool = true}) { ... }

So now, it is more tricky, because a parameter can be optional and/or nullable. I would say that a parameter is optional if it either has a default parameter or if the type is nullable (or both).

This feels dubious. I think nullability and optionality should be orthogonal. (With, of course, the caveat that a non-nullable optional parameter needs some default value so that it doesn't get the default default of null.)
 

Syntax for named parameters

Now there is one other thing. You may wonder, why do you propose the {...} syntax for named parameters in Dart 2.0? And why a colon `:` instead of the more natural `=` in the call? Well, there is a reason, I want to propose also the following. Named parameters are just sugar.

  enableFlags(bold: true, hidden: false);

This would desugar to writing:

  enableFlags({"bold": true, "hidden": false});

Yes, this is a map. Named parameters are sugar for writing a map. So actually, all parameters are positional now, where the last one can be a Map that can be written with some sugar as named parameters.

This is an interesting idea, but I don't think it works with static typing. Consider:

foo(Map<String, bool> flags) {
  enableFlags(flags);
}

How do we know that the required "bold" parameter was provided? How do we know that there aren't extra parameters?

Despite what Dart's syntax tries to imply, I don't think there is any real correspondence between named parameters and maps. Likewise, there's no correspondence between positional parameters and lists.

The real correspondence, if there were one, should be that positional parameters correspond to a tuple and named ones to a record. Lists and maps assume all elements have the same type and the size is not fixed. Neither is the case for parameters. (It would be true for rest parameters, if we had those in Dart...)


So you may wonder, does this really matter anything? Who would write the later? Well, I think it actually does matter, because it helps making it much more natural to write javascript interop, because in javascript devs often add an option object (Map in Dart) as the last parameter. This object just serves as providing a bunch of named/optional parameters. With this proposal, this would map very straight forward to Dart.

Can we generalize this?

If you think about it, this kind of asks to give the Map object to have a more flexible type. Now with this proposal, here:

enableFlags({bool bold, bool hidden}) { ... }

This says now, that the enableFlags function has as parameter a Map that must have two keys, the string bold and the string hidden, both of type bool. So if we can declare the type of the Map so specific here, why not in other Dart code? 

What about this syntax (assuming right hand types in Dart 2.0):

var map: Map<{"bold": bool, "hidden": bool}> = {"bold": true, "hidden": false};

Well this syntax seems quite okay, I think we can provide a very natural sugar for this? Let's look a second at the syntax for named parameters:

enableFlags({bold: bool, hidden: bool}) { ... }
enableFlags(bold: true, hidden: false);

It seems clear to me that the sugar should look like;

var map: {bold: bool, hidden: bool} = (bold: true, hidden: true);

Okay, so we have here a very specific kind of Map object, where all the keys are strings. Of course this is just a plain javascript object. And this should map to a plain javascript object when compiled. This would even give more advantages with javascript interop. For example, you could give much more auto completion and type safety to the react package.

Right. Now you're talking about record types. :)

Adding those to the type system and then mapping named parameters to them is an interesting idea. I'm not philosophically opposed to it, but it's a lot of complexity. Given that Dart skews pretty hard in the nominal typing direction, I don't know if structurally typed records would be a good fit.

Cheers!

– bob



Bob Nystrom

unread,
Aug 18, 2016, 2:29:42 PM8/18/16
to General Dart Discussion
On Thu, Aug 18, 2016 at 7:43 AM, krupal shah <krupals...@gmail.com> wrote:
For example, take String and bool, or List and int. If there is no primitive type in Dart, why some types are written in lower case and others in upper case.

There is no internally consistent reason for the names of those types in Dart. In other words, if Dart was the only language you ever learned, those names would make no sense.

You could almost say that lowercase names are for sealed types that users can't be implemented. Except that "String" is sealed and "num" is implemented by "int" and "double". You could also almost say that lowercase names are for fixed-size value types that can be safely stored inline. Except that "int" isn't fixed-size in Dart.

The names are externally consistent in that they are cased similar to how corresponding types in some other languages are named. Both internal and external consistency help make your language easier to learn: they let users leverage things they already know. Balancing the two is tricky.

Personally, I never liked the names. :-/
 
I would also like that Dart 2.0 remove the ugly ternary expressions and pre-post increment operators. Nobody should write those unreadable stuff.

Supporting a feature doesn't mean users have to use it. There's a lot of value in having similar features to other languages even when you also have better alternatives. The former let new users be productive quickly even before they've fully learned the new language.
 
Go is much cleaner than Dart currently IMHO.

Yes, they definitely swung much harder towards internal consistency at the expense of external consistency than we did.

Cheers!

– bob 


Filip Hracek

unread,
Aug 18, 2016, 2:39:43 PM8/18/16
to mi...@dartlang.org
This is the classic Performance vs Preference dilemma. It's nicely explained here (emphasis mine):
 
The QWERTY layout was designed to
prevent the jamming of mechanical
arms on early typewriters. The Dvorak
layout, by contrast, was designed to
maximize typing efficiency: it grouped
keys based on frequency of use, and
positioned keys to promote alternating 
 
keystrokes between hands, among
other refinements. The result is a
30 percent improvement in typing
efficiency, and claim to most of
the world records for speed typing.
Despite the clear advantages of the
Dvorak design, QWERTY enjoys the 
 
 
following of generations of people
trained on the layout, which in turn
drives manufacturers to continue
perpetuating the standard. Dvorak
wins on performance, but QWERTY
wins on preference. 

Let's say you're a keyboard manufacturer and you can only build one keyboard. Do you choose Dvorak (performance) or QWERTY (preference)?

Kasper Peulen

unread,
Aug 18, 2016, 3:13:22 PM8/18/16
to Dart Misc
> Using a postfix "?" is pretty neat, but I worry that it's visually very similar to syntax we probably want for nullable types.

Yeah, the first proposal is only for if there is NNBD. Otherwise, I would put the question mark on the type.

> This feels dubious. I think nullability and optionality should be orthogonal. (With, of course, the caveat that a non-nullable optional parameter needs some default value so that it doesn't get the default default of null.)

If you have NNBD, I think optional should mean, "has a default value'. That is the most logical way of reasoning about it. If it is non nullable, and has no default value, it is required. If nullable, and has no default value, I think it should have an implicit default value of null, and therefore, it is optional. You could also enforce this to be explicit, but I think other languages (Swift, ES6 + Flow) also do it like this.

In this example:
say(msg: String, from: String?, device: String = 'phone') -> String 

The second one, is optional, because it has default value null, the third one is optional, because it has default value 'phone'.


> Right. Now you're talking about record types. :)
> Adding those to the type system and then mapping named parameters to them is an interesting idea. I'm not philosophically opposed to it, but > it's a lot of complexity. Given that Dart skews pretty hard in the nominal typing direction, I don't know if structurally typed records would be a > good fit.

I have not much experience with tuples or record types. Never heard of record types to be honest. But I do have some experience lately with descructering Arrays or Objects in ES6 with Flow types. I think what I propose is actually, exactly this. The syntax for ES6 "named parameters", is what I propose here for what named arguments are, with a slightly nicer syntax. I'm not a fan of javascript, but I think destructering is actually a brilliant feature. Because a named argument is no special language construct. It is just a plain javascript object.

Now together with Flow, you can also type an javascript Object, with the exact same syntax I'm also proposing to type a Map. So I assume that is a record type? I think it is really interesting to have that in Dart as well. Especially, because Dart compiles to Javascript in the end. And doing interop with libraries that use this feature extensively, like React and Redux would be much easier.

Anyway, I was getting a bit wild in the end, if I'm realistic, I don't see it land in Dart anytime, I just find this ES6 feature super interesting, and I use it all the time at work. But let's focus more on named/optional parameters in this topic.

tatumizer-v0.2

unread,
Aug 18, 2016, 3:15:07 PM8/18/16
to Dart Misc
That's how swift does it, and I think it's GOOD (scroll to "Omitting External Parameter Names"):
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html

Bob Nystrom

unread,
Aug 18, 2016, 3:16:17 PM8/18/16
to General Dart Discussion

On Thu, Aug 18, 2016 at 11:38 AM, 'Filip Hracek' via Dart Misc <mi...@dartlang.org> wrote:
This is the classic Performance vs Preference dilemma. It's nicely explained here (emphasis mine):

That's a little different, I think (though it's a good article!).

In this case, there is no (known) user performance difference between the way the core library types are cased now versus other ways they could have been cased. It's just that users need to learn their names correctly and we're trying to minimize the effort it takes for them to do that. The two ways we can do that are:
  • Let a user's knowledge of how they are named in another language teach them how they are cased in Dart.
  • Let a user's knowledge of how some names are cased in Dart teach them how others are.
In both cases you're trying to reduce the amount they have to learn—the delta between what they know today and what they'll know once they know all the names. There's just different ways to get there. You can think of it like applying dictionary compression to the delta. Do we user other languages for our compression dictionary or not?

– bob

Don Olmstead

unread,
Aug 18, 2016, 3:58:50 PM8/18/16
to mi...@dartlang.org
I've certainly mentioned my dislike of the current named/optional syntax. I haven't programmed swift but at a glance the additional names seem to be kinda goofy. What are the benefits over something like C#'s setup https://msdn.microsoft.com/en-us/library/dd264739.aspx

krupal shah

unread,
Aug 18, 2016, 3:59:33 PM8/18/16
to mi...@dartlang.org


On Thursday, August 18, 2016 at 11:59:42 PM UTC+5:30, Bob wrote:

On Thu, Aug 18, 2016 at 7:43 AM, krupal shah <krupals...@gmail.com> wrote:
For example, take String and bool, or List and int. If there is no primitive type in Dart, why some types are written in lower case and others in upper case.

There is no internally consistent reason for the names of those types in Dart. In other words, if Dart was the only language you ever learned, those names would make no sense.

You could almost say that lowercase names are for sealed types that users can't be implemented. Except that "String" is sealed and "num" is implemented by "int" and "double". You could also almost say that lowercase names are for fixed-size value types that can be safely stored inline. Except that "int" isn't fixed-size in Dart.

The names are externally consistent in that they are cased similar to how corresponding types in some other languages are named. Both internal and external consistency help make your language easier to learn: they let users leverage things they already know. Balancing the two is tricky.

Personally, I never liked the names. :-/
 
         I assume you mean Java in place of ' other languages'. But there is a reason for UpperCase objects and lowercase primitives in Java.

 
I would also like that Dart 2.0 remove the ugly ternary expressions and pre-post increment operators. Nobody should write those unreadable stuff.

Supporting a feature doesn't mean users have to use it. There's a lot of value in having similar features to other languages even when you also have better alternatives. The former let new users be productive quickly even before they've fully learned the new language.
 
 
          But it also means that some users 'may write' it and others will have to read it. Why does Dart want to be familiar with any other language?

tatumizer-v0.2

unread,
Aug 18, 2016, 4:36:33 PM8/18/16
to Dart Misc
> I haven't programmed swift but at a glance the additional names seem to be kinda goofy
Swift is not afraid to look goofy, b/c it has more self-confidence.

If you look at examples of their APIs, you will see that most natural external names of parameters are not the same as internal names (not entirely unexpected: whatever is a floor for one can be a ceiling for another). This makes invocations sound more like English sentences. I came to appreciate it after initial WTF period.

C#'s solution is a simplistic one (same as in most languages),  Addition of an optional named parameter potentially breaks compatibility, unless you make it the last one - which may not be a good place for it logically. And, of course, the subtlety of 2 different perspectives (external and internal) was not discovered by C#.

To be sure, swift has problems of its own (e.g Strings are unusable). Some proposals for swift ver 4 look extremely odd to me - the impression is that the language got a bit dizzy from its success, and started moving swiftly into la-la land. (I'm afraid Dart 2 will follow suit - the disease is contagious!)

Don Olmstead

unread,
Aug 18, 2016, 4:59:09 PM8/18/16
to mi...@dartlang.org
Its probably because I haven't worked in Swift but I've yet to encounter a situation where I would want a name that doesnt make sense inside the scope and outside of it.

Does any other language have that sort of setup? Might just be because I'm more familiar with C variants.

tatumizer-v0.2

unread,
Aug 18, 2016, 5:14:18 PM8/18/16
to Dart Misc
There're plenty of examples in all their APIs. For the most part, it revolves around prepositions (like in natural language) e.g.
mutating func replaceRange<C : CollectionType>(_subRangeRange<Int>, with newElementsC) 

From the outside, it's named "with". From inside - "newElements".

I'm not sure who discovered it. But certainly, the philosophical idea that things  look differently from outside than they do from inside is not especially new. 

Bob Nystrom

unread,
Aug 18, 2016, 5:47:01 PM8/18/16
to General Dart Discussion

On Thu, Aug 18, 2016 at 1:59 PM, Don Olmstead <don.j.o...@gmail.com> wrote:
Does any other language have that sort of setup?

Swift inherited it from Objective-C which inherited it from Smalltalk. In Smalltalk, methods that take more than one argument have keywords between them. The keywords are part of the method name, not the names for the parameters that the arguments get bound to in the body. The parameters have their own names that are not part of the method's API.

– bob

Don Olmstead

unread,
Aug 18, 2016, 6:43:01 PM8/18/16
to mi...@dartlang.org
Ah yes its been forever since I did any Smalltalk. Thanks Bob!

kc

unread,
Aug 19, 2016, 9:59:40 AM8/19/16
to Dart Misc
A delta with bad habits and cruft would be a good thing.

K. 

kc

unread,
Aug 19, 2016, 10:02:09 AM8/19/16
to Dart Misc
Swift 3.0 has been cleaned up re first param and moves away from the Obj C/Smalltalk roots.

See 'Consistent first argument labels':

K.
Reply all
Reply to author
Forward
0 new messages