Skip to content

Meta: Support Some Form of Static Typing? #4563

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
xixixao opened this issue Jun 7, 2017 · 10 comments
Closed

Meta: Support Some Form of Static Typing? #4563

xixixao opened this issue Jun 7, 2017 · 10 comments
Assignees
Labels
Milestone

Comments

@xixixao
Copy link
Contributor

xixixao commented Jun 7, 2017

Let's preface this with:

@jashkenas: FWIW [...] I feel very, very strongly about not adding "static" types to CoffeeScript. Period.

Keeping that in mind, a scenario in which I think we could support some type system:

  1. CoffeeScript does not have a type system. Neither static nor dynamic, besides what JS has already.
  2. There should be minimal perf impact.
  3. We should not have to add any more dependencies to the compiler.
  4. If you don't want to use types, we will never force you, and we will not compromise the syntax of CoffeeScript for this support.

What I think we could do is support a syntax that could be used by an existing type system. Afaik there are two popular existing choices in ES6 ecosystem: TypeScript and Flow (add more if you know of any).

No reason to beat around the bush: TypeScript will be much harder to support. It's got more extra syntax to JS.

I imagine that the following code would be possible:

foo = (x: ?number): string ->
  x ? "default string"

Which with Flow would report an error.

I think it might be interesting to figure out the upsides and downsides here and if there is any interest in having this supported. At this point it's very theoretical, as I'm not even sure how much effort it would take to support all of the typing syntax.

@GeoffreyBooth
Copy link
Collaborator

I’m working on a PR to improve support for comments, to allow comments to “follow” the next or previous token into the final output. So something like this:

foo = (x###: ?number###)###: string### ->
  x ? "default string"

foo = function (x/*: ?number*/)/*: string*/ {
  return x || "default string";
}

The same logic could apply to backticks (embedded JS).

There are many problems to solve to get this to work. For one thing, it’s more or less straightforward to attach a comment (or embedded JS block) to a ”literal” token like ), or to an identifier like x, as these are output pretty much as is. The issue is what to do when attaching to a token like ->; should the comment be next to the { or next to the function? Or a comment in the middle of something like for item in items by 3, where the source tokens have no direct corollaries in the output. This may not be solvable.

If it is solvable, though, or solvable even for limited cases (literal and identifier tokens only, for example) then this could be used to enable the comment-based Flow syntax, or with backticks could enable the regular Flow syntax. And there’s nothing Flow- or TypeScript- or static types-specific about this, so it would pass muster in terms of not muddying up the language. It just makes comments and embedded JS more versatile in that you can use them in more places. Currently you can only use comments or embedded JS where statements are allowed (i.e. on a line by itself) rather than anywhere interspersed with other code.

@xixixao
Copy link
Contributor Author

xixixao commented Jun 7, 2017

@GeoffreyBooth Although I think allowing comments and backticks in those places would be good and useful, I don't think it is a solution for the type system support. Even wrapping in backticks would be too onerous and would prevent tooling being built on top. So let's keep in mind that some limited support might come from this approach, but focus the discussion here on a more full-fledged support.

foo = `<T>`(x`: T`)`: T` -> x

would not get much love imho.

@GeoffreyBooth GeoffreyBooth changed the title [CS2] Support Some Form of Static Typing Meta: Support Some Form of Static Typing? Jun 11, 2017
@GeoffreyBooth
Copy link
Collaborator

This related thread is worth reading: coffeescript6/discuss#12

One thing we could agree on in that thread was that if there’s a way to implement static types via comments and Flow now, without any changes to the compiler, someone should write up an explainer on how to do that and we should add that info to the docs. Obviously such an implementation isn’t as convenient as purpose-built types support, but until such an implementation ever gets built, we might as well document what people can achieve today. Especially if such an implementation never gets built as part of the CoffeeScript compiler itself, as opposed to a preprocessor or a fork.

Such an exercise will also help illuminate what an ideal typing system and syntax would look like, especially if there’s a non-trivial app (like the Todos example) implemented with it. It would give us a good starting point for discussion along the lines of “okay so we have this, and what are its deficiencies? What should we add to the compiler to make this experience better?”

And if it’s not possible to implement even the comments-based version of Flow support, then that’s another issue. I suspect that our current support for comments is insufficient to enable that syntax, and that’s what I’m trying to achieve in my comments branch. But more generally, this would point to a separate deficiency in the compiler, that our support for comments isn’t as robust as it could be; and that’s something we should fix regardless of how people feel about static types.

In the meantime, @xixixao do you want to try seeing if you can implement Todos using comments-based Flow? Or any other static types system you think might work with CoffeeScript 2 as it is today. And if you’re able to achieve that, we can use that example as our baseline.

@xixixao
Copy link
Contributor Author

xixixao commented Jun 11, 2017

@GeoffreyBooth TypeScript doesn't support comment syntax at all. Flow doesn't work with generics (which is arguably a Flow bug, but might not be resolvable):

###:: declare f: <T>(a: T): T###
f = (a) -> a
5:   return a;
            ^ T. This type is incompatible with
4: f = function(a) {       ^ some incompatible instantiation of `T`

^ another problem is that the scoping could be wrong, because CoffeeScript scoping works differently to JS.

As I wrote in my previous comment, using comments or backticks would be too onerous for practical use. I don't think there is any value in documenting what you can currently do, because no one is going to use it.

@GeoffreyBooth
Copy link
Collaborator

In #4572 I’ve added documentation for how to use comments with Flow for static type checking. It’s not as elegant as TypeScript or regular Flow, but I don’t think it’s so onerous that people wouldn’t use it; what do you think @xixixao?

If inline comments like ###: string ### strike you as too bulky, you could always write a preprocessor that converts some other simplified syntax into these comments before the CoffeeScript compiler does its work.

@boris-petrov
Copy link

@GeoffreyBooth - that's pretty amazing! I hate the idea of writing such things as comments instead of as syntax, but it's better than nothing for now! When are we going to have a release with this?

Just a question - wouldn't it be better to have some actual syntax for type annotations in CoffeeScript and then transpile that down to TypeScript? I understand that that's a lot more work but isn't that the "best" that could be done?

@xixixao
Copy link
Contributor Author

xixixao commented Jul 25, 2017

@GeoffreyBooth

If inline comments like ###: string ### strike you as too bulky, you could always write a preprocessor that converts some other simplified syntax into these comments before the CoffeeScript compiler does its work.

What syntax? It can't clash with CoffeeScript. That's whole crux of the problem...

@xixixao
Copy link
Contributor Author

xixixao commented Jul 25, 2017

Not a fan of the documentation, as it doesn't align with the elegance of CoffeeScript, but it's not factually incorrect. It's unlikely I'll have the time to look into this any time soon, but someone else could pick it up.

@GeoffreyBooth
Copy link
Collaborator

@xixixao A preprocessor could convert something like #: string # to ###: string ###, for example. That would probably wreak havoc with your syntax highlighting, so you’d probably be better off choosing something else. But my point was that some preprocessor step could convert anything recognizable into the comment-based syntax before the CoffeeScript compiler sees it. For example:

fs = require 'fs'
CoffeeScript = require 'coffeescript'

code = fs.readFileSync process.argv[1]
code = code.replace /#:(.+)#/, '###:$1###'
code = CoffeeScript.compile code, {bare: yes, header: no}
console.log code

This is obviously very minimal. I’m sure if you invested some time you could create a preprocessor that enables a more elegant syntax, that you convert into the comment-based syntax before the CoffeeScript compiler gets to it. Because your step would happen before the CoffeeScript compilation, you don’t need to worry about clashing with CoffeeScript’s syntax, as my example here does.

You could even go further, and write a postprocessor that converts these comments into something else. That’s essentially what flow-remove-types does. Such a postprocessor could conceivably convert comments into TypeScript type annotations, for passing to the TypeScript checker rather than Flow. That’s why I invested a lot of time into #4572, to enable such add-ons. As you wrote above, there’s no appetite for adding static types to CoffeeScript proper, so this is the next best thing. I agree it’s inelegant, but until someone implements a better solution, I feel like we should at least document this in case anyone wants static types with CoffeeScript.

@boris-petrov There are no plans at the moment for adding type annotations to CoffeeScript directly. Any workarounds will involve including the annotations within backticks or comments, possibly coupled with pre- or postprocessors to improve readability. If you build such an add-on, I would be happy to link to it from the docs. I’m planning to release #4572 with 2.0.0-beta4, which should come out as soon as #4572 and #4607 are merged in.

@GeoffreyBooth
Copy link
Collaborator

Static types are now possible via #4572. Clearly there are more elegant solutions possible, but the specific problem posed by this issue is resolved. Please open new issues for other implementations of static types you would like to discuss.

@GeoffreyBooth GeoffreyBooth added this to the 2.0.0 milestone Aug 3, 2017
@GeoffreyBooth GeoffreyBooth self-assigned this Aug 3, 2017
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants