var values = ["call","me","maybe","?",true];
bool|String value = values[i];if(value || value:length > 3) { ... }
Bird|(Dog&!Dalmatian) pet = getPet(); pet:bark(); pet:sing();
// In a Vector class.
Vector|Matrix multiply(Vector|num value) {
... your code here ..
}
PersonModel& model = getPersonModel();
model.name = "Dart"; // Defined in PersonModel.
model.title = "Mr."; // "Dynamic" field.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. }
int|null a = getANumberOrNull();
print(a:); // If a is null just ignore the whole print call.
> 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
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.
> 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
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.
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.
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").
foo(bar) {bar:blah();}
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.
(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.
var i = ...if (i is Foo) {i.fooMethod(); // <-}
if (i is Foo && i is Bar) {i.fooMethod();i.barMethod();}
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?
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
> 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
> 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.
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.
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.
zip = lottery.drawWinner?().address?.zipcode
var zip, _ref;
zip = typeof lottery.drawWinner === "function" ? (_ref = lottery.drawWinner().address) != null ? _ref.zipcode : void 0 : void 0;
[obj release];
obj?.release();
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 "?".
If memory serves, dart_analyzer already supports type inference from "is" in different branches of an "if".
--
Michael
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.
foo([arg]) => print('${?arg}');foo(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?
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.