samuelgoto / proposal-optional-types

A proposal for an optional type system for JS.
81 stars 2 forks source link

Summarized Discussion Notes #16

Open DanielRosenwasser opened 7 years ago

DanielRosenwasser commented 7 years ago

Earlier last week @SamuelGoto, @ajklein, and @dimvar arranged a meeting with us on the TypeScript team to gather our feedback on the proposal. We ended up covering three primary concerns. Hopefully I've done the conversation justice with this summary.

Incompatibility Issues

When we discussed exploring optional types in JavaScript between our teams, a few different options were proposed. Those options included something like the following.

  1. Find the common subset of type systems for JavaScript and implement that.
  2. Find a way to incorporate features from all the type systems using explicit syntax to enforce specific semantics where type systems differ.
  3. Come up with something entirely different that is inspired by existing type systems that is not a "subset".
  4. Standardize on one of the existing type systems.

I'll try to go one-by-one on why we remain fairly skeptical that any of these approaches is the right one for users.

  1. The common subset is too minimal because the overlap in semantics is so different. For example, what is the overlap between TypeScript's, Flow's, and Closure's logic for how types are inferred and narrowed? One can trivially find an example that works in one type system but not the other.

    Furthermore, which subset of TypeScript would one target? TypeScript in strictNullChecks mode? In noImplicitAny mode? noImplicitThis? noStrictGenericChecks? Each stricter mode has huge benefits, but many of our new users pick more lax options because of the ability to more gradually adopt existing JS code.

    From our discussion, it seems that in seeking a subset, you end up having to give up much of what makes each type system useful. This compromises the user experience, and it's not obvious who would want a fourth "subset" type system.

  2. Explicit syntax (e.g. ~Foo for structural vs !Foo for nominal) is, besides being a burden for users, able to address only so many of the larger differences between the type systems.

    For example, how does one indicate that he or she wants all bindings within a function to be inferred through unification?

  3. It is not clear what something entirely different would involve; however, anything sufficiently different from any one existing type system would be doing users a disservice because of the confusion it would cause. Users would immediately ask why TypeScript or Flow deviate from the "standard" agreed-upon type system, and ultimately this would hurt each language's community. There was discussion that TypeScript could "target" this new type system while coexisting, but what purpose would that serve? TypeScript would not only have to understand the semantics of this new type system, it would have to understand the mapping between each construct. TypeScript would likely continue to emit untyped JS code because of the marginal benefits between the two targets.
  4. The fact that multiple type systems exist for JavaScript is a testament to the fact that users and teams have different needs and interests. For example, it's not clear what the willingness would be by other teams to even standardize TypeScript itself, and even so I'm not sure it's the best idea given how rapidly the type system continues to evolve.

Standardization Issues

We keep a close eye on what our community has been asking for. Besides the above points points, standardization hasn't been something TypeScript users have demanded as heavily from our team as other meaningful improvements to the type system. We have heard from users that being able to run typed JS directly in runtimes is often useful for prototyping/iteration scenarios, but otherwise standardization has not been one of our most prevalent asks. As we discussed, and I'll point out below, standardization is not necessarily a pre-requisite for being able to run in the browser.

I also mentioned above that standardization is a concern from an iteration & evolution standpoint. Adjusting the type system for the better is something TypeScript has been able to do, but with standardization, we would collectively relinquish much of the ability to amend and improve existing behavior.

User Experience and Use-Case Concerns

One of the primary use-cases in the current proposals seems to be issuing runtime errors and potentially static checking (each within in-browser developer tools) as well as the ability to immediately run typed JS code.

We did have some concerns from our team about when this checking occurs, and what these errors are, and how they'd be surfaced. A line of code like

let x: string = 10;

is something that most users would expect a typed language to error on; however, it sounds like only developer tools would ever surface the errors as imagined now.

If we believe that users want to run type-annotated code in the browser for debugging & iteration purposes (and our teams do), then we believe it's worth exploring a plugin system for developer tools so that language servers could be integrated to give a better experience. Code could be compiled on the fly in much in the same way that systemjs allows running TypeScript code. Also note that prior to joining TypeScript, one member of our team worked with Chromium's dev tools team to provide something very similar to this plugin model.

This approach is something that many modern editors already take - they are reasonably decoupled from their supported languages to serve multiple different experiences. For users, that could be as simple as downloading a dev tool plugin for their browser that could support TypeScript, Flow, or anything else.

samuelgoto commented 7 years ago

Thanks for the thoughtful and detailed write up @DanielRosenwasser , really good points. I'm going to circulate this with some of the early reviewers to gather more eyes here and try to distill this feedback into something that we can act on, let me report back here.

dimvar commented 7 years ago

Thanks for the reply Daniel.

In the section "Incompatibility Issues" you bring up two distinct issues IMO.

The first is that the standard type system would be different from any of the existing ones, even if it is based heavily on them. I agree that this is true, and the question is how it will affect users. During the meeting, the TypeScript team mentioned that they would not want to create backwards incompatibilities for existing TS users. This means that you would not want to change TS's semantics, so the only option would be to compile to typed JS. Then, as you mention, TS users would ask why does TS deviate from the standard type system, and you'd like to avoid that. Is it correct then to conclude that the TS team would not support any proposal for an optional unsound type system for JS, not just this particular one? Let me know if I'm misunderstanding your position.

The second issue is the technical difficulties that arise when trying to standardize a JS type system. You bring up inference, and narrowing of types, and I think Anders also brought up recursive structural types. I would also add generics, unions, and aliasing to the list. I share this concern, and I don't have a good answer about how hard it would be to spec these things. I'd like to believe that it is feasible, just by the fact that there are three implementations that handle these features, but yeah, we'd have to try and see. It was mentioned in the meeting that the TS spec does not try to account for all these subtleties, and ultimately the source of truth is the TS compiler. With respect to inference specifically, we are not proposing to infer types of unannotated variables. So, the unification example you bring up would not be a problem. But we still require some amount of inference, in order to narrow the declared type of a variable after an IF test.

I also mentioned above that standardization is a concern from an iteration & evolution standpoint.

This is always a trade-off in a standardization decision. Can you explain why, in this particular case, you believe that the costs of standardization outweigh the benefits?

Last, one of the main problems we would hope to tackle with standardization is fragmentation. We are trying to avoid the creation of silos where, e.g., the Closure-style JS is not compatible with TypeScript JS, or with Flow JS. Then, people won't be able to use a TypeScript library in Flow code, etc. Can you add a few words about the position of the TypeScript team on this? Do you believe that it will be a non-issue in practice?

Thanks again for chatting with us. Even if this proposal ends up not moving forward, I think it is quite valuable to document the stumbling blocks, for people who want to try again in the future.

DanielRosenwasser commented 7 years ago

Is it correct then to conclude that the TS team would not support any proposal for an optional unsound type system for JS, not just this particular one?

At this time, we don't believe it's realistic for anyone to fairly champion an existing type system with universal consensus, nor do we believe an alternative type system would be beneficial to our users. This is not to say that we don't think this should ever happen, but more simply, that any decision now would be jumping the gun.

With respect to inference specifically, we are not proposing to infer types of unannotated variables. So, the unification example you bring up would not be a problem.

Then this is sort of like the subset type system described above. We need to ask ourselves: why exactly would a user want to use a type system that works this way? As we've discussed above, such semantics would be too limited for users to find truly useful. One of the reasons today's JS type systems have proliferated is that each performs some amount of non-trivial inference for functions and local variables.

This is always a trade-off in a standardization decision. Can you explain why, in this particular case, you believe that the costs of standardization outweigh the benefits?

To clarify, ability to iterate is a major reason, but it's not the reason we see issues with standardization. Keep in mind the other concerns I've mentioned above. If those concerns were addressed then the topic would certainly be worth revisiting, but given that they're not, it's not even clear that there would be benefits.

Then, people won't be able to use a TypeScript library in Flow code, etc. Can you add a few words about the position of the TypeScript team on this? Do you believe that it will be a non-issue in practice?

We do have some concerns about duplicated work and incompatible type declarations among other checkers, but it simply seems like any solutions to this suffer from some of the same problems discussed above.

samuelgoto commented 7 years ago

On "Find a way to incorporate features from all the type systems using explicit syntax to enforce specific semantics where type systems differ."

Explicit syntax (e.g. ~Foo for structural vs !Foo for nominal) is, besides being a burden for users,

Yep, agreed that it would be quickly hard for users to use. One of the things to be explored is whether we could use this approach to be a common compilation target for flow/typescript to solve fragmentation (i.e. a compilation target meant to be produced by machines, not users).

able to address only so many of the larger differences between the type systems. For example, how does one indicate that he or she wants all bindings within a function to be inferred through unification?

Can you expand this a bit further? Example?

samuelgoto commented 7 years ago

On "Come up with something entirely different that is inspired by existing type systems that is not a "subset"."

Users would immediately ask why TypeScript or Flow deviate from the "standard" agreed-upon type system, and ultimately this would hurt each language's community

That's certainly an interesting question to be asked for the typescript/flow community, but could you try to expand this a bit further thinking from the perspective of the JavaScript / web developer community? Wouldn't we benefit a lot more people if we optimized for the larger JavaScript community (you may argue that this optimization isn't the right one, and that's ok and Id love to hear why not, but I just wanted to frame our thinking effectively, representing where most users are)?

samuelgoto commented 7 years ago

The fact that multiple type systems exist for JavaScript is a testament to the fact that users and teams have different needs and interests. For example, it's not clear what the willingness would be by other teams to even standardize TypeScript itself, and even so I'm not sure it's the best idea given how rapidly the type system continues to evolve.

I think it is entirely reasonable to argue that TypeScript/Flow or any other type system isn't mature enough to be standardized, and that's a very satisfying answer: if it is evolving a lot, it is not ready to be baked in, simple as that.

I think it is an entirely different design model though to advocate for "different teams that have different needs and interests under multiple type systems". I don't actually know what's the right answer here, I just think these are completely different lines of development (maturity versus the core property of multiple/plugable type systems versus a single unified one).

Like I said, I don't know with confidence what's the right direction for the latter discussion, but my intuition is that there is a lot more precedent for single-unified-type-systems (e.g. C, C#, Java, C++, and any other major statically typed language) than pluggable type systems (any example outside of academia?).

I'd love to discuss both of these points further, but I wanted to acknowledge that these (maturity and pluggable type systems) are orthogonal points.

samuelgoto commented 7 years ago

We did have some concerns from our team about when this checking occurs, and what these errors are, and how they'd be surfaced.

These seem fairly simple to address? I mean, I don't have an answer off the top of my head, but these don't seem like massive problems that we wouldn't be able to find convergence on?

is something that most users would expect a typed language to error on; however, it sounds like only developer tools would ever surface the errors as imagined now.

As currently formulated, this would be caught by both the offline transpilers as well as the runtime type checkers. No loss of abilities here.