-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Messages that include union types are pretty much impossible to read #20906
Comments
cc @kasperl. |
Set owner to [email protected]. |
This comment was originally written by [email protected] Ideas for a better [toString()] for union types? We currently have {A,B} Some languages use A|B We could go with something more verbose like A or B or some type in {A,B} or union of A and B or one of A or B or an unknown type that is either A or B or ??? For the hint message itself, we could change it completely to something like none of the possible argument types here (A or B) can be assigned to the parameter type C |
Issue #20957 has been merged into this issue. |
I suggest you avoid the concept of union types altogether, and pick concrete types that leads to problems. For example: Instead of saying: The argument type '{BadInterfaceType,BadTypedefType,TypedefType,MalformedType,InterfaceType}' cannot be assigned to the parameter type 'GenericType' Say something like this: The type of the expression "expr" can have the type "MalformedType" which isn't assignable to the type of "foo", "GenericType". At the same time, I think it is important to realize that "parameter" vs "argument" is a distinction you shouldn't expect most programmers to get. |
This comment was originally written by [email protected] Added Started label. |
This comment was originally written by [email protected] I've changed the definition of union-type subtyping to make such messages much less common, and I've changed the union-type string representation to be more user friendly. The definition of union-type subtyping is now {A1, ..., An} <: B if Ai <: B for any [i]. Before we required this for all [i], which seems more sound, but does not really have the desired effect because assignment compatibility is defined by A <=> B if A <: B or B <: A and we define A <: {B1, ..., Bn} when A <: Bi for any [i]. CL: https://codereview.chromium.org/578643004/ I changed the string representation from {A1, ..., An} to (A1 or ... or An) which is hopefully more intuitive. CL: https://codereview.chromium.org/576403002 |
This doesn't sound like a solution to me. The diagnostic message talks about a singular type, and you jam in multiple types. You need to explain to the user what is wrong, and why it is wrong. Reusing a diagnostic message doesn't seem to accomplish this. cc @lukechurch. |
This comment was originally written by [email protected] OK, looks like we're stuck then. As I see it, there are three options:
Details
var x; results in the type of [x] being [(A or B)]. This seems reasonable to Outstanding issues here include the UI for examples like class A { where there are conflicting possibilities for the method [m]. The x.m : ((int or double)) -> (int or double) because this works well in the following common case: var x; Alternative approaches include saying that x.[] : ((int) -> int or (int) -> double) but this is probably much harder to fit into the current
However, using a custom lub would almost surely have the effect of var x = 0; Of course, I think there are some nice ways out here, but they are
var x = 0;
dynamic x = 0; and the hint goes away because [x] is marked as "anything Added Waiting label. |
Re-writing the relevant messages shouldn't be that hard. For example, instead of the original The argument type '{BadInterfaceType,BadTypedefType,TypedefType,MalformedType,InterfaceType}' what if we were to say None of the possible types of the argument can be assigned to the parameter type 'GenericType' and then fix the hover text so that the propagated type displays, much like you were suggesting for error messages, as One of BadInterfaceType, BadTypedefType, TypedefType, MalformedType, or InterfaceType |
I don't think there's anything intuitive about "union" types. Claiming that they're simple and intuitive doesn't make it so, and we deliberately avoided these kinds of things to keep the Dart type system simple. Regarding Brian's suggestion, I'd like to understand how that information will be presented in the command line version of the analyzer. |
This comment was originally written by [email protected] There are a lot of things getting mangled together here that need to be separated. I also think that we're not getting anywhere near as much user value out of the underlying technology here as we could be doing. I'll send out a vc invite to discuss. |
This comment was originally written by [email protected] I don't think there's anything unintuitive about "union" types. Claiming that they're not simple nor intuitive doesn't make it so, and we deliberately added these kinds of things to keep the Dart analyzer useful. But seriously, you have not explained why you think they're unintuitive; "Peter says so" is not an argument. Can you explain why it's not intuitively obvious that [x] has type [A] or [B] after the if-statement here: var x; I don't think writing "[A or B]" instead of "[A] or [B]" suddenly makes things unintuitive. Also, in case you did not read the Facebook JS type system thread, Facebook's JS typs system has union types: https://www.youtube.com/watch?v=Bse0sYR7oVY&t=83m36s So, there's a precedent, for what it's worth. |
Here's some more:
The only optionally typed languages I know of off-hand that don't have union types are ActionScript, TypeScript, and Dart. TypeScript has a limited form of them by allowing method overloading in interfaces and also leans heavily towards statically typed variables so you don't run into union cases as often from what I gather. |
I'm not sure what you mean by intuitive in this context, It's a fairly overloaded word, could you give me a little more detail as to what you mean? |
@collinsn I did not start making claims that union types are a "simple, intuitive concept", you did. It is you that is making unfounded claims, and I'm objecting to that. I'm not objecting to the analyzer using union types internally. In fact, I've been long arguing for the analyzer needing a more complex inference algorithm, where it can track information that cannot be represented in source code. However, I also know from previous experience just how much you can confuse users when you expose the internal details of type inference to users. My experience comes from being the javac tech lead at Sun Microsystems just after we shipped JDK 5. That mean that I saw all the incoming language and compiler bugs. I also spoke a conferences, and was involved in various communities. I've also helped a lot of my coworkers around issues in the Java type system. Based on this, I've formed some opinions on what I think is intuitive, and what isn't. Here are a few guidelines that I think will serve our users: * Do not expose inferred types that the user haven't written directly. * Do not expose inferred types that cannot be expressed in the language. * Don't trust type theoreticians (or Ph.D. students) who claim that a concept is simple. Most likely, they haven't made any studies whatsoever to back their claims. They have probably lost touch with how people outside the field of type theory think, and might have forgotten they aren't targeting people with a degree in CS. How do I apply these guidelines? Let's examine the message originally reported: "The argument type '{BadInterfaceType,BadTypedefType,TypedefType,MalformedType,InterfaceType}' cannot be assigned to the parameter type 'GenericType'" I've looked at the listed types, and the issue is that MalformedType typed isn't a subtype of GenericType. All the other types are. So I claim this would be a better message: "The expression 'type' might have the type 'MalformedType' which can't be assigned to 'GenericType'." Then I'd add this additional information about line 2019: type = checkNoTypeArguments(type); "The method 'checkNoTypeArguments' may return a 'MalformedType'." I'm really surprised that you're arguing that it is a better approach to use abstract concepts that aren't part of the language instead of concrete actionable information like this. Let's revisit if union types are intuitive, I know they aren't intuitive because they confuse me. They also confuse you: "The definition of union-type subtyping is now {A1, ..., An} <: B if Ai <: B for any [i]." I believe this is a type rule for intersection types, and you're now proposing to infer intersection types. I'm not sure that is the right approach. The members of an intersection type A & B is the union of all the members of A and B. Because only types that implement both A and B are in the set. The members of an union type A | B is the intersection of all the members of A and B. Because type that implement either A or B are in the set. And this brings me back to my experience, most times when I hear people talking about union types, they're actually talking about intersection types. So I can object to when you say they're intuitive, and I can make the counterclaim that they aren't. But it is a weird discussion to have. We're not discussing if we should add union types to Dart (even if Bob thinks we are), we're discussing if the messages produced by the analyzer are confusing. |
This comment was originally written by [email protected] My internship ends on Friday and we still haven't agreed on a UI, so I've gone ahead and put the union types behind a flag ([enableUnionTypes]) and disabled them by default. I've also bumped the milestone to 1.8 and changed the owner to @lukechurch (he had some UI ideas and was interested in doing some experiments). Instructions for enabling union types are included in the CL description: https://codereview.chromium.org/592923002/ In another CL, not yet landed, I'm making the semantics of union types controllable by another flag ([strictUnionTypes]). This will help with @lukechurch's experiments. The default is non-strict / permissive union types, which have intersection-type semantics. The non-strict semantics are more in the spirit of Dart's other "if it might make sense we allow it" rules, e.g. the assignment compatibility rule for interface types: A <=> B iff A <: B or B <: A However, our inference is only sound for the strict semantics. For example var x; Using the permissive / intersection-type semantics, e.g. saying that methods defined on [A] or on [B] are defined on [x] after the join point is obviously unsound, but we choose this as the default because it minimizes false positives (hints when there is not actually any problem). For example, using the strict / union-type semantics where a method is only defined on [x] if it's defined on both [A] and [B], we get a false positive for code like this: class A { I expect that in the common case the strict semantics are more useful -- and indeed, it's easy enough to refactor the above code to eliminate the warning, by moving the method call into the if-statement -- but the rule for analyzer hints seems to be "thou shalt not generate a false positive", because there's no way to disable a hint. In a GVC with @lukechurch and @brianwilkerson we discussed ways forward, coming away with two plans:
Using the proof tree, we can provide a much more helpful message to the user. This should help address @ahe's concern about saying more clearly what the problem is. This will probably be more useful with the strict semantics. E.g., for the code 1 class A { we compute a proof tree like ----- Assignment line 10 ----- Assignment line 12 and then can give a very helpful hint like line 16: the method [m] may not be defined on [x] because [x] may have type [B]
Questions this will help answer include "how often are the hints useful?". As mentioned, I'm making the strict vs permissive semantics controllable by a flag, so that it's easy to experiment with both versions of the analysis. Set owner to @lukechurch. |
This comment was originally written by [email protected] Strict union types internally are here: https://codereview.chromium.org/599293002 Strict union types externally in the completion UI are next ... |
This comment was originally written by [email protected] Strict union types externally are here: |
Union types were removed a long time ago. I don't believe that this issue is relevant any more. |
Username: kasperl
The error / hint messages that include union types are pretty much impossible to read and understand.
The argument type '{BadInterfaceType,BadTypedefType,TypedefType,MalformedType,InterfaceType}' cannot be assigned to the parameter type 'GenericType'
It's not clear that this is really a union type.
////////////////////////////////////////////////////////////////////////////////////
Editor: 1.7.0.dev_02_00 (2014-09-10)
OS: Linux - amd64 (3.13.0-35-generic)
JVM: 1.7.0-google-v6
projects: 1
open dart files: 3
auto-run pub: true
localhost resolves to: 127.0.0.1
mem max/total/free: 1778 / 887 / 304 MB
thread count: 29
index: [737502 locations, 265 sources, 80240 elements]
Attachment:
screenshot.png (366.55 KB)
The text was updated successfully, but these errors were encountered: