Union types in Dart?

4,006 views
Skip to first unread message

Daniel Rodríguez

unread,
Sep 5, 2012, 9:36:00 PM9/5/12
to mi...@dartlang.org
Hi, I am new to the list. I just wanted to share an idea.

Given that dart is optionally typed, we can do a lot with vars and Dynamics, or concrete types, but maybe we can also use something in the middle, and I think dart has a lot of potential here for union types. This is what I am proposing:

var values = ["call","me","maybe","?",true];
bool|String value = values[i];
Where bool|String means exactly that. A bool or a String. Any other type will fail if we run in check mode. Naturally bool|String is the same as String|bool. 

And then we can do:

if(value || value:length > 3) {
  ...
}

Where the suspicious : means that if value has a property called length it will be invoked, otherwise the expression is going to be ignored and we will return null instead. Note that the rest of the expression is ignored and thus a null pointer expression is not possible. This is just a shorthand for type checks (i.e. using "is"). 

I am not trying to force any particular syntax. I just wanted to get more information, and see if this was considered and discarded, or it is still a possibility. I think this could be very powerful, and sadly, also confusing.

Another example:

Bird|(Dog&!Dalmatian) pet = getPet();
pet:bark();
pet:sing(); 

That means that I want a Bird or a Dog that does not extend or implement the Dalmatian class.  If we want, we can simply define a new superclass or interface, and that is all good, this simply allows for rapid experimentation. Naturally in dart we could simply say var, but that puts us all the way up to Dynamic, so a Snake would pass in check mode. I am not saying that this kind of code would be a joy to work with, but it opens up possibilities for better tools and IDEs.

And of course, this could be used in ways that will create code that could become difficult to understand:

// In a Vector class.
Vector|Matrix multiply(Vector|num value) {
  ... your code here ..
}

But the programmer can already do the same by defining the method using var. So we are not adding any new ways of shooting yourself in the foot here. Just a way to do it in a better documented and more complicated way. Using a Bazooka.

After doing some very research I found that this was already brought up in the mailing list. It would be interesting if this could work with the Mixin proposal that is in the works. 

Also, I wonder if it's possible to use this to inform tools about classes that have a set of defined properties, and a bunch of optional fields on an object. This would be useful for Json and Model objects. I know that the idea is to only allow reflection though the Mirrors API, so I don't know if this would work, but:

PersonModel& model = getPersonModel();
model.name = "Dart";  // Defined in PersonModel.
model.title = "Mr."; // "Dynamic" field.

Where the & means: "This has all the properties of PersonModel and more". Tools can use this info to avoid presenting warnings if a dynamic property is used, and still present code completion for the properties defined in PersonModel.

PersonModel& person;
if (person is PersonModel && person:title != null) {
  print ("Hi " + person.title + " " + person.name); // Pardon me for using this super terribly illegal way of concatenating strings. 
}

Lastly, Nullable types could be:

int|null a = getANumberOrNull();
print(a:); // If a is null just ignore the whole print call.

Yeah, I know I am introducing another issue here. So "a:" means "a:anyField" and is used to check if a is null or not. Probably went too deep.

What do you guys think? Will it blend?






Ladislav Thon

unread,
Sep 6, 2012, 12:11:46 AM9/6/12
to mi...@dartlang.org


> What do you guys think? Will it blend?

It likely won't. IIRC, this was already discussed and it didn't look like it would get into Dart in foreseeable future.

Also, I personally think that your proposal is way too ambitious. I think that plain union types would be enough, without intersection types (I've never missed those) or some weird : or & operators.

Back in the days, I was able to add nullable types into the VM and it wasn't hard (modifying the parser, adding a flag into AbstractType and running a check in the checked mode). Union types would be harder, but not impossible.

LT

Dirk Detering

unread,
Sep 6, 2012, 2:35:54 AM9/6/12
to mi...@dartlang.org

Am 06.09.2012 03:36 schrieb "Daniel Rodríguez" <seth.i...@gmail.com>:
> Given that dart is optionally typed, we can do a lot with vars and Dynamics, or concrete types, but maybe we can also use something in the middle, and I think dart has a lot of potential here for union types.

Seems that you're into Ceylon? ;-)
The suggestion that Dart can do more with types is interesting. But I have some doubts about what I read here.

Union types are very difficult to use properly without sound pattern matching.

> This is what I am proposing:
>
> var values = ["call","me","maybe","?",true];
> bool|String value = values[i];

I don't like that example. The list is untyped and could likewise contain e.g. an int. This would crash late at runtime, perhaps even only in some circumstances, presuming the list may not be literal but filled programatically before.

If you expect something to be of some type this should be documented as early as possible in the data flow, thus right on the list.

> Where bool|String means exactly that. A bool or a String. Any other type will fail if we run in check mode. Naturally bool|String is the same as String|bool. 
>

NB: I don't like the type expression in type position. Makes the whole typing thing even more weird. Think:

Map<String, MyModelClass> ¦ List<SomeOtherModelClass> myval = getVal();

When Dart provides type expressions it must also extend typedef beyond function types, so expressions can be aliased.
(Note: don't have a proper pipe sign on my mobile, it seems)

> And then we can do:
>
> if(value || value:length > 3) {
>   ...
> }
>
>
> Where the suspicious : means that if value has a property called length it will be invoked, otherwise the expression is going to be ignored and we will return null instead.

First: Please not the colon here! A safe dereference is more suggestive when using a question mark (my opinion).

Second: The safe dereference could return null instead of crashing, providing always predictable values, like in Groovy. This is the semantic of safe dereference.
But ignoring a whole expression will make reasoning about the code extremely difficult. What means "ignoring" btw?

> Note that the rest of the expression is ignored and thus a null pointer expression is not possible.

Is it not? What exactly is ignored? only the > 3 part?
Why? Why isn't it the whole condition, including the complete OR expression?
How do you know where to stop the ignoring?
If you ignore the comparision: what value is or-ed with the first part?

How do you know that the expression is exhaustive?

String¦Person¦Circle myvar = getVar();

if ( myvar:length > 3 ¦¦ myvar:radius > 5)

What's the value of the condition if you get a Person?

>This is just a shorthand for type checks (i.e. using "is"). 

No. It is much more than that. It is magically providing some 'else' if the type check fails, but that 'else' seems out of control of the user.

> I am not trying to force any particular syntax. I just wanted to get more information, and see if this was considered and discarded, or it is still a possibility. I think this could be very powerful, and sadly, also confusing.
>
> Another example:
>
> Bird|(Dog&!Dalmatian) pet = getPet();
> pet:bark();
> pet:sing();
>
>
> That means that I want a Bird or a Dog that does not extend or implement the Dalmatian class.  If we want, we can simply define a new superclass or interface, and that is all good, this simply allows for rapid experimentation. Naturally in dart we could simply say var, but that puts us all the way up to Dynamic, so a Snake would pass in check mode. I am not saying that this kind of code would be a joy to work with, but it opens up possibilities for better tools and IDEs.
>

The last sentence I do not understand. What do you think programming languages are about?

> And of course, this could be used in ways that will create code that could become difficult to understand:
>
> // In a Vector class.
> Vector|Matrix multiply(Vector|num value) {
>   ... your code here ..
> }
>
>
> But the programmer can already do the same by defining the method using var. So we are not adding any new ways of shooting yourself in the foot here. Just a way to do it in a better documented and more complicated way. Using a Bazooka.
>
> After doing some very research I found that this was already brought up in the mailing list. It would be interesting if this could work with the Mixin proposal that is in the works. 
>
> Also, I wonder if it's possible to use this to inform tools about classes that have a set of defined properties, and a bunch of optional fields on an object. This would be useful for Json and Model objects. I know that the idea is to only allow reflection though the Mirrors API, so I don't know if this would work, but:
>
> PersonModel& model = getPersonModel();
> model.name = "Dart";  // Defined in PersonModel.
> model.title = "Mr."; // "Dynamic" field.
>
>
> Where the & means: "This has all the properties of PersonModel and more". Tools can use this info to avoid presenting warnings if a dynamic property is used, and still present code completion for the properties defined in PersonModel.

To be honest, I do not grok the usecase for this.
When I write variable.method then I expect the object in variable to have that method, either implicitly by simply writing the call, or explicitly by typing the variable. I would never mix it in the shown way.
Otherwise I would be saying "I expect a subtype of PersonModel, and I do not say which one, but I expect it to have some specific method nevertheless".  Very unsound.

>
> PersonModel& person;
> if (person is PersonModel && person:title != null) {
>   print ("Hi " + person.title + " " + person.name); // Pardon me for using this super terribly illegal way of concatenating strings.
> }
>
>
> Lastly, Nullable types could be:
>
> int|null a = getANumberOrNull();
> print(a:); // If a is null just ignore the whole print call.
>

Once again: This ignoring can get fairly complex and hard to reason about, especially when more variables are involved and/or the code results in a value.

Nullable types are a very special case IMHO, and they need special handling, e.g. with safe dereference.

>> Yeah, I know I am introducing another issue here. So "a:" means "a:anyField" and is used to check if a is null or not. Probably went too deep.

Far too deep, in my mind, sorry.

Ladislav Thon

unread,
Sep 6, 2012, 10:48:56 AM9/6/12
to mi...@dartlang.org


> The suggestion that Dart can do more with types is interesting.

Is it? Seems to me that passing objects of different classes as an argument to a single method is pretty common in dynamically typed languages. Being able to express it in the type system would be useful, if you ask me.

> Union types are very difficult to use properly without sound pattern matching.

Are they? What is the difference between saying "this parameter can be a String or an int" in the documentation comment or in the declaration? Do you need pattern matching in the first case? Because the first case is already possible in Dart (and there are people doing it!).

> Far too deep, in my mind, sorry.

Agree with most of your other comments. The original post tried to reach very far and in quite ad hoc way. Something simpler I'd love to see.

LT

Dirk Detering

unread,
Sep 6, 2012, 12:16:18 PM9/6/12
to mi...@dartlang.org

Am 06.09.2012 16:49 schrieb "Ladislav Thon" <lad...@gmail.com>:
>
>
> > The suggestion that Dart can do more with types is interesting.
>
> Is it? Seems to me that passing objects of different classes as an argument to a single method is pretty common in dynamically typed languages. Being able to express it in the type system would be useful, if you ask me.
>

Right. So we agree that this suggestion is interesting, don't we?

> > Union types are very difficult to use properly without sound pattern matching.
>
> Are they? What is the difference between saying "this parameter can be a String or an int" in the documentation comment or in the declaration? Do you need pattern matching in the first case? Because the first case is already possible in Dart (and there are people doing it!).
>

Ok, perhaps I wrote this too spontaneous, but:
The difference is that the machine is totally agnostic to such comments. The comments can be a big lie; the machine acts based on the code alone ("acts" also in the sense of speaking to me).
But I expect the machine to do something useful with things I express on the language level, if only warning me "hey, that looks stupid!"
So the type declaration should be in a way that we both -I and the machine- agree on the sense of it.
In case of union types that means: I tell what types I expect and you (machine) then tell me if I have considered all relevant cases in the code, or if valid parameters (according to type) can lead to bad effects.
Thus union types are best used in combination with case differentiation for each possible type, so we can more easily reason and agree about possible effects.

NB: I suppose code commented in a way you mentioned does some sort of manually programmed "pattern matching" in the body, i.e. a combination of type check and value check.
But I don't expect the checker to understand that
value.length is wrong inside 'if (value is int)' branch, but correct in 'else' branch.

> > Far too deep, in my mind, sorry.
>
> Agree with most of your other comments. The original post tried to reach very far and in quite ad hoc way. Something simpler I'd love to see.

Ok.

Dirk Detering

unread,
Sep 6, 2012, 12:27:03 PM9/6/12
to mi...@dartlang.org

Am 06.09.2012 18:16 schrieb "Dirk Detering" <mail...@googlemail.com>:
> But I don't expect the checker to understand that
> value.length is wrong inside 'if (value is int)' branch, but correct in 'else' branch.
>

Thinking about it, that was silly. There's no reason why the checker should not keep track of that.

Christopher Wright

unread,
Sep 6, 2012, 1:02:43 PM9/6/12
to mi...@dartlang.org
I think almost all language proposals that don't come from the core design team or the compiler team will be rejected. It's hard to convince someone of the necessity of something when they have previously not chosen to do that, and when changing that decision will cost them a fair bit of effort.

It is easier to accept a suggestion from someone who is already working on the project. They have more sympathy about the difficulty of changing things (spec, dart2js, VM, tests, editor, documentation, release notes...those all have to line up) and are showing they're willing to take on at least some of the cost.

This means that the exact same suggestion from two different people might have significantly varying results, which is unfortunate.


--
Consider asking HOWTO questions at Stack Overflow: http://stackoverflow.com/tags/dart
 
 

Bob Nystrom

unread,
Sep 6, 2012, 1:05:01 PM9/6/12
to mi...@dartlang.org
Apologies to Ladislav and Dirk for thread-forking, but I want to reply to Daniel's original post...

On Wed, Sep 5, 2012 at 6:36 PM, Daniel Rodríguez <seth.i...@gmail.com> wrote:
Hi, I am new to the list. I just wanted to share an idea.

Given that dart is optionally typed, we can do a lot with vars and Dynamics, or concrete types, but maybe we can also use something in the middle, and I think dart has a lot of potential here for union types. This is what I am proposing:

var values = ["call","me","maybe","?",true];
bool|String value = values[i];
Where bool|String means exactly that. A bool or a String. Any other type will fail if we run in check mode. Naturally bool|String is the same as String|bool. 

Union types have come up a couple of times before. I think Gilad is fond of them, but doesn't feel they're worth the complexity they add to the language. Dart's type system is designed with the understanding that it's willing to sacrifice expressiveness for simplicity. Because the language semantics don't rely on static types, Dart is comfortable with letting you write code that does stuff outside of what the type system can understand.

Things like "this function returns a number or a string" are a case of that: the type system can't express it, but the language can.

Figuring out where to draw the line of what the type system can and can't express is one of the harder parts of language design. It pretty much boils down to a judgement call and so far, Gilad and co. have said no to union types (and intersection types for that matter).


And then we can do:

if(value || value:length > 3) {
  ...
}

Where the suspicious : means that if value has a property called length it will be invoked, otherwise the expression is going to be ignored and we will return null instead. Note that the rest of the expression is ignored and thus a null pointer expression is not possible. This is just a shorthand for type checks (i.e. using "is"). 

Something like a safe navigation operator has come up a few times. It's something that I could maybe see being added to the language later, but probably not any time soon. Our general philosophy is to push off any language features until later that we can. That keeps things simple now. Languages grow over time anyway, so there's little need to rush into adding lots of features early on.

Syntactically, ":" is unlikely to work well here. As Larry Wall says, "everyone wants the colon". We already use ":" for labels, named arguments, and maps. Using if for another thing is asking for ambiguity. For example:

foo(bar) {
  bar:blah();
}

Is this:

1. Safely calling "blah" on "bar" if it supports it?
2. Defining a label "bar" and then invoking the function "blah"?

I also worry that there are some semantic problems here. What does "has a property" mean in the context of noSuchMethod()?


I am not trying to force any particular syntax. I just wanted to get more information, and see if this was considered and discarded, or it is still a possibility. I think this could be very powerful, and sadly, also confusing.

Another example:

Bird|(Dog&!Dalmatian) pet = getPet();
pet:bark();
pet:sing(); 

That means that I want a Bird or a Dog that does not extend or implement the Dalmatian class.  If we want, we can simply define a new superclass or interface, and that is all good, this simply allows for rapid experimentation. Naturally in dart we could simply say var, but that puts us all the way up to Dynamic, so a Snake would pass in check mode. I am not saying that this kind of code would be a joy to work with, but it opens up possibilities for better tools and IDEs.

And of course, this could be used in ways that will create code that could become difficult to understand:

// In a Vector class.
Vector|Matrix multiply(Vector|num value) {
  ... your code here ..
}

But the programmer can already do the same by defining the method using var. So we are not adding any new ways of shooting yourself in the foot here. Just a way to do it in a better documented and more complicated way. Using a Bazooka.

After doing some very research I found that this was already brought up in the mailing list. It would be interesting if this could work with the Mixin proposal that is in the works. 

Also, I wonder if it's possible to use this to inform tools about classes that have a set of defined properties, and a bunch of optional fields on an object. This would be useful for Json and Model objects. I know that the idea is to only allow reflection though the Mirrors API, so I don't know if this would work, but:

PersonModel& model = getPersonModel();
model.name = "Dart";  // Defined in PersonModel.
model.title = "Mr."; // "Dynamic" field.

Where the & means: "This has all the properties of PersonModel and more".

In an OOP language with open subtyping, a type annotation already implies "and more". If I have a variable of type "PersonModel", it's understood that it can always be some subtype of that that defines other stuff too.
 
Tools can use this info to avoid presenting warnings if a dynamic property is used, and still present code completion for the properties defined in PersonModel.

It's certainly more verbose, but you can always get that now by explicitly casting to dynamic in the places where you want to avoid warnings. One way to do that is:

(model as Dynamic).title = "Mr.";


PersonModel& person;
if (person is PersonModel && person:title != null) {
  print ("Hi " + person.title + " " + person.name); // Pardon me for using this super terribly illegal way of concatenating strings. 
}

Lastly, Nullable types could be:

int|null a = getANumberOrNull();
print(a:); // If a is null just ignore the whole print call.

My old null-safety proposal for Dart used that same idea. I personally really like this, but the language designers don't seem to be too keen on non-nullable types.

The following is just my opinion, so don't consider it to be representative of the Dart team whatsoever:

I actually think union types (and maybe even intersection types) make a lot of sense for Dart. My strong hunch is that, if it doesn't already, the Editor will eventually support them in its type analysis engine. It's already doing type inference based on "is", so in code like:

var i = ...
if (i is Foo) {
  i.fooMethod(); // <-
}

On the marked line, the Editor knows the type of i is Foo. But now consider:

if (i is Foo && i is Bar) {
  i.fooMethod();
  i.barMethod();
}

It would be great (and correct) if you didn't get any static warnings here, but the only way to make that work is if the Editor understands intersection types. Likewise, imagine:

class Foo {
  void methodInBoth() { ... }
}

class Bar {
  void methodInBoth() { ... }
}

if (i is Foo || i is Bar) {
  i.methodInBoth();
}

Again, you ideally don't want a static warning here, and the Editor will need union types to make that fly. So my hunch (and it's just a hunch) is that the Editor will support union and intersection types. Then the question, if it does, why not give the user a syntax to let them annotate them?

Cheers,

- bob

Ladislav Thon

unread,
Sep 6, 2012, 2:31:37 PM9/6/12
to mi...@dartlang.org

> Again, you ideally don't want a static warning here, and the Editor will need union types to make that fly. So my hunch (and it's just a hunch) is that the Editor will support union and intersection types. Then the question, if it does, why not give the user a syntax to let them annotate them?

Ooh, that's _very_ true! Here's one of great features of Ceylon -- all types that the compiler can use in its reasoning about the program can be expressed directly in the language. Now Dart isn't statically typed, so something like this makes little sense, but I still love the idea.

LT

Ladislav Thon

unread,
Sep 6, 2012, 2:38:48 PM9/6/12
to mi...@dartlang.org


> It is easier to accept a suggestion from someone who is already working on the project. They have more sympathy about the difficulty of changing things (spec, dart2js, VM, tests, editor, documentation, release notes...those all have to line up) and are showing they're willing to take on at least some of the cost.
>
> This means that the exact same suggestion from two different people might have significantly varying results, which is unfortunate.

This is true and it also makes sense. But to defend my standpoint a little bit, I said on several occasions that I already implemented nullable types in the VM (I guess it can still be found on GitHub) and while it was more of a prototype than production implementation, it took me no more than 2 or 3 hours, so from implementation point of view, I'd consider nullable types a no-brainer. Union types -- I'd guess that for a prototype, one day would be enough.

LT

John Messerly

unread,
Sep 6, 2012, 2:41:40 PM9/6/12
to mi...@dartlang.org
On Thu, Sep 6, 2012 at 11:31 AM, Ladislav Thon <lad...@gmail.com> wrote:

> Again, you ideally don't want a static warning here, and the Editor will need union types to make that fly. So my hunch (and it's just a hunch) is that the Editor will support union and intersection types. Then the question, if it does, why not give the user a syntax to let them annotate them?

Ooh, that's _very_ true! Here's one of great features of Ceylon -- all types that the compiler can use in its reasoning about the program can be expressed directly in the language. Now Dart isn't statically typed, so something like this makes little sense, but I still love the idea.

I think the Editor's analyzer already has union types. There was a bug in the error messages once, and it was printing types that looked suspiciously like union types :)

Justin Fagnani

unread,
Sep 6, 2012, 4:22:44 PM9/6/12
to mi...@dartlang.org
On Thu, Sep 6, 2012 at 10:05 AM, Bob Nystrom <rnys...@google.com> wrote:
Apologies to Ladislav and Dirk for thread-forking, but I want to reply to Daniel's original post...

On Wed, Sep 5, 2012 at 6:36 PM, Daniel Rodríguez <seth.i...@gmail.com> wrote:
Hi, I am new to the list. I just wanted to share an idea.

Given that dart is optionally typed, we can do a lot with vars and Dynamics, or concrete types, but maybe we can also use something in the middle, and I think dart has a lot of potential here for union types. This is what I am proposing:

var values = ["call","me","maybe","?",true];
bool|String value = values[i];
Where bool|String means exactly that. A bool or a String. Any other type will fail if we run in check mode. Naturally bool|String is the same as String|bool. 

Union types have come up a couple of times before. I think Gilad is fond of them, but doesn't feel they're worth the complexity they add to the language. Dart's type system is designed with the understanding that it's willing to sacrifice expressiveness for simplicity. Because the language semantics don't rely on static types, Dart is comfortable with letting you write code that does stuff outside of what the type system can understand.

That's an understandable line of reasoning in general, especially if viewing the language in isolation, but Dart lives in a web world and dart:html already uses "overloading" which would be more explicit for tools and users if Dart had union types. Right now there's a convention of naming parameters after the types that it can accept joined by _OR_. 

drawImage(CanvasElement | ImageElement | VideoElement imageSource) would be better (to me at least) than drawImage(canvas_OR_image_OR_video). it's longer, but at least there's a chance for the editor to give meaningful warnings.

I think that's a good indication that union types might be worth their cost.

Of course, another option, in some cases at least, is more interfaces, but that only works for classes under your control. Whatever happened to interface injection?

2¢...

-Justin


mythz

unread,
Sep 6, 2012, 4:59:15 PM9/6/12
to mi...@dartlang.org
Something like a safe navigation operator has come up a few times. It's something that I could maybe see being added to the language later, but probably not any time soon. Our general philosophy is to push off any language features until later that we can. That keeps things simple now. Languages grow over time anyway, so there's little need to rush into adding lots of features early on.

That's a great philosophy to have, but NullRefExceptions are one of the most common errors in modern programming today and having something like CoffeeScript's null-soaking operator would make it a lot easier to avoid this, e.g:

zip = lottery.drawWinner?().address?.zipcode

The intent here is as it reads, i.e. to capture whether a zip code exists in a nested object graph.

This is the manual mechanics CoffeeScript saves you, which is a lot less readable, error prone and hides the intent of the logic.

var zip, _ref;
zip = typeof lottery.drawWinner === "function" ? (_ref = lottery.drawWinner().address) != null ? _ref.zipcode : void 0 : void 0;

The extra effort and boilerplate is going to prevent developers from defensively programming in this way, where they otherwise would've used the null soaking operator and have prevented a runtime error.

Objective-C is another language that lets you send messages (aka call methods) on null targets (instances), e.g:

[obj release];

Likewise It would also be nice to do:

obj?.release();

In the end I think this is a really nice feature to have sooner rather than later.

- Demis

Daniel Rodríguez

unread,
Sep 6, 2012, 7:16:38 PM9/6/12
to mi...@dartlang.org
Thanks all for your comments. As I stated in the original post, it was not my intention to make a formal proposal (at least not now), just to share my thoughts. 

Dart's new syntax to check for optional parameters uses ?, so definitely using something similar to coffeescript's syntax would make sense. But this problems of which expressions to execute and how does this work with noSuchmMethod remain.

Now, going against my own proposal here, but in the case of parameters to functions that can have multiple types as in the drawImage(canvas_OR_image_OR_video) case, why not use named parameters instead? The new {} syntax seems very nice. And checking which ones where provided is easy enough with "?".

Of course that doesn't solve the problem for return values or container elements. I know the language is getting ready for M1 and I am sure changes of this nature will not occur for a while, if they occur at all, but the discussion is nice.

--
Consider asking HOWTO questions at Stack Overflow: http://stackoverflow.com/tags/dart
 
 



--
Daniel Rodríguez

Paul Brauner

unread,
Sep 7, 2012, 3:16:23 AM9/7/12
to mi...@dartlang.org
On Fri, Sep 7, 2012 at 1:16 AM, Daniel Rodríguez <seth.i...@gmail.com> wrote:
Thanks all for your comments. As I stated in the original post, it was not my intention to make a formal proposal (at least not now), just to share my thoughts. 

Dart's new syntax to check for optional parameters uses ?, so definitely using something similar to coffeescript's syntax would make sense. But this problems of which expressions to execute and how does this work with noSuchmMethod remain.

Now, going against my own proposal here, but in the case of parameters to functions that can have multiple types as in the drawImage(canvas_OR_image_OR_video) case, why not use named parameters instead? The new {} syntax seems very nice. And checking which ones where provided is easy enough with "?".


You cannot easily enforce the invariant that exactly one of them is provided.

Michael Hendricks

unread,
Sep 7, 2012, 8:25:38 AM9/7/12
to mi...@dartlang.org

If memory serves, dart_analyzer already supports type inference from "is" in different branches of an "if".

--
Michael

Bob Nystrom

unread,
Sep 7, 2012, 5:17:30 PM9/7/12
to mi...@dartlang.org
On Thu, Sep 6, 2012 at 4:16 PM, Daniel Rodríguez <seth.i...@gmail.com> wrote:
Thanks all for your comments. As I stated in the original post, it was not my intention to make a formal proposal (at least not now), just to share my thoughts. 

Dart's new syntax to check for optional parameters uses ?, so definitely using something similar to coffeescript's syntax would make sense. But this problems of which expressions to execute and how does this work with noSuchmMethod remain.

Right, though keep in mind that the new prefix "?" operator in Dart is distinctly different from null-check operators in other languages. It doesn't inspect the value, it relies on some other out-of-band knowledge to tell if an argument was actually explicitly passed. If you do this:

foo([arg]) => print('${?arg}');
foo(null);

It will print "true" (because arg was passed) and not "false", even though the value of arg is null.
 

Now, going against my own proposal here, but in the case of parameters to functions that can have multiple types as in the drawImage(canvas_OR_image_OR_video) case, why not use named parameters instead?

Like Paul said, named parameters make it hard to ensure exactly one value is passed.

Also, in these cases, these methods are coming from the DOM, which is language-agnostic. The DOM IDL is defined in a pseudo-language that does support overloading by type, so this weird "_OR_" stuff is our best attempt to try to match that existing API in a language that doesn't allow overloading but does have types. (In contrast, JS can handle this even though it lacks overloading because it doesn't have type annotations either, so the overloads just collapse to a single method whose implementation inspects the types at runtime).

Not having overloading but having type annotations sticks us between a rock and a hard place here.
 
The new {} syntax seems very nice. And checking which ones where provided is easy enough with "?".

Yes, that's the idea.
 

Of course that doesn't solve the problem for return values or container elements. I know the language is getting ready for M1 and I am sure changes of this nature will not occur for a while, if they occur at all, but the discussion is nice.

Yup, we won't be making any language changes for a while.

Cheers!

- bob
Reply all
Reply to author
Forward
0 new messages