Skip to content
This repository has been archived by the owner on Feb 19, 2018. It is now read-only.

CS2 Discussion: Features: Type Annotations #12

Closed
rattrayalex opened this issue Jul 23, 2016 · 50 comments
Closed

CS2 Discussion: Features: Type Annotations #12

rattrayalex opened this issue Jul 23, 2016 · 50 comments

Comments

@rattrayalex
Copy link
Contributor

I've assumed it'd be next to impossible to add type annotations to CoffeeScript, but thought we should open an issue to discuss if it might be possible. Static Typing is undergoing a renaissance of sorts these days, with developers flocking to new statically typed languages and adding static annotations to dynamic languages like Python (see PEP484) and JavaScript (via Flow and TypeScript).

Flow supports gradual typing for JavaScript, and has some decent tooling to make development easier. They have loose plans to ingest an ESTree AST, which could allow integration of Flow and CS6.

I don't think TypeScript would be a viable target for CS6, as it doesn't play well with Babel (a probable target of CS6) and requires "complete type coverage", which wouldn't likely gel well with CoffeeScript developers, who don't like typing (pardon the pun).

While this would be awesome, I struggle to see how it could work, at least with Flow-like syntax and readability:

obj: {[id:string]: number} = 
  thing: 7
myFn(a: []number, b: number): []number ->
  a.map(x -> x + b)
myFnThatTakesACallback(a: number, cb: (id: number) => Promise<number>): Promise<number> => 
  cb(a).then(x => x + 1)
@realJoshByrnes
Copy link

realJoshByrnes commented Jul 24, 2016

Types? K.I.S.S.

(String a) = ->
  return "You said #{ a }"

The resulting JS would have to be something like:

function () {
  testType(String, a);
  return "You said " + a;
}

and inserted up top would be something like:
testType = function(type, data) { if (data !instanceof type) throw TypeError 'Expected type' }

Thoughts?

@rattrayalex
Copy link
Contributor Author

Static types are definitely preferable to runtime types, as they can be useful during development. Flow has a lot of tooling built around it already, and performs a lot of valuable, complex inference work.

@rattrayalex
Copy link
Contributor Author

It'd also be nice to keep the typing syntax as close to Flow/TypeScript as possible, given that they are the community standards for typed JavaScript.

@dadleyy
Copy link

dadleyy commented Jul 27, 2016

I'm not too inclined to see a type system make it into this project. I think it would be a significant departure from the original language and takes away some of the simplistic beauty of it. Type systems are amazingly helpful in compiled languages like go and c++ but in browser-side applications there is a lot of power in weak typing; I'm thinking about error-first callbacks that either accept an Error or Boolean type as their first argument, and that distinction indicates the successfulness of the function. It would be very cool if there was a strongly typed "sister-language" that could come out of this project but the important distinction is that it would be separate (think scss and sass maybe?)

@MemoryChips
Copy link

MemoryChips commented Jul 27, 2016

In typescript there is a type 'any' which I would guess covers your issue.
To leave out typing is a mistake IMO. Angular 2 uses typescript to good effect. You would have trouble getting me to go back to coffeescript without a type system.
Rob

@rattrayalex
Copy link
Contributor Author

rattrayalex commented Jul 28, 2016

@dadleyy I think if you familiarize yourself with Flow and/or TypeScript, you will find that your concerns do not apply.

@JimPanic
Copy link
Contributor

Flow looks really nice, but I'd have to (again) get used to inline type specs.

Elixir handles this in a different way that is really easy on the eyes as it doesn't clutter the actual function signature, but you only annotate it:

defmodule LousyCalculator do
  # ...
  @spec add(number, number) :: number_with_remark
  def add(x, y), do: {x + y, "You need a calculator to do that?"}
end

http://elixir-lang.org/getting-started/typespecs-and-behaviours.html

Maybe it is possible to incorporate Flow with Coffeescript in the long run, though? (Instead of implementing our own…)

@dadleyy
Copy link

dadleyy commented Jul 28, 2016

@rattrayalex I guess as long as any sort of typing is optional my concerns are moot but that needs to be the case, otherwise we're talking about a not-insignificant departure from the original coffeescript (and es6 too). Would there be any sacrifices/breaking-changes to the language's grammar because of typing?

I have to imagine that building a single compiler that supports optional typing is much more difficult to build than one without typing at all though, and holding up support for all the es6 goodies we're trying to bring to coffeescript because sometimes we want to add types to our symbols seems like a bummer. What if however, while building the new compiler if we were to keep tying in mind and make it easy to extend the compiler to support them and later created some sort of coffeescript++ typed compiler? Supporting two compilers would be a nightmare I suppose though. I just think we can come up with a solution wherein typeless coffeescript users are not in anyway effected by the desire to add types.

Flow seems like it's very much a "post-processing" system; wouldn't a tool like that just belong in a codebase's build system after having compiled coffeescript into js? In the same regard, isn't solid test coverage using tools like jasmine/mocha also a good defense against the problems that a typing system try to solve?

@rattrayalex
Copy link
Contributor Author

rattrayalex commented Jul 28, 2016

@dadleyy again, I think it'd be very helpful if you familiarized yourself with Flow (read the docs, install, add the types to a toy project, read up on the benefits of static typing, etc).

But, yes, I should be explicit that types would be both optional and gradual. This issue is to add support for Flow for those who want to use it, not to require usage of Flow for all users.

For example, Babel has flow support built-in (enabled via a plugin) but most Babel users do not use Flow.

@carlsmith
Copy link

Worth mentioning that we could maybe reuse some ideas from ASM here. It converts this C code...

int f(int i) {
  return i + 1;
}

...into this JavaScript...

function f(i) {
  i = i|0;
  return (i + 1)|0;
}

@carlsmith
Copy link

It's also worth mentioning that once you add a feature, everyone has to use it, whether they want to or not. You can't just opt out of a feature, unless you can disable it entirely, as other people will use it, and you will need to read their code.

I can decide not to use an English word in my own sentences, but I still have to use the word whenever someone else includes it in one of their sentences. Just reading a word is still using it.

@MemoryChips
Copy link

MemoryChips commented Jul 28, 2016

You may prefer to not see types in other peoples code but I think it helps the readability.

In Typescript, you can set the compiler to implicit any which which allows you to avoid the requirement for types in your code.
In my opinion if Coffeescript6 does not have type checking, it will lose much of its potential users base.

Rob

@rattrayalex
Copy link
Contributor Author

@carlsmith - in some languages, types are required everywhere. For example, you can't write any Java code without using types.

I'm not proposing this kind of requirement for coffeescript.

@carlsmith
Copy link

I know??

I was responding to @ozjd, where he said "The resulting JS would have to be something like"...

function () {
  testType(String, a);
  return "You said " + a;
}

We may not need to have type-checking functions if we use ASM style type-casting, and let the JS engine throw errors. Perhaps not though. I haven't thought this through, but it seemed worth mentioning, as it might solve at least some of the problems that would come up if we added typed variables to CoffeeScript.

I also responded to your comment that types would be optional, which is a perfectly valid point, but people often confuse that with meaning that you don't have to use them if you don't want to, which is a bit of an overstatement. We would still be complicating the language for every user.

Personally, I'm totally against adding static types to CoffeeScript. You can't change anything more fundamental than a language's type system. The original proposal was for type hints, so the compiler would treat them like comments, except that they are in the AST for other tools to work with. They have no semantics, only syntax. I'm not in favour of type hints, but am happy to discuss it and see if anyone can make a compelling case for them. Statically typed CoffeeScript is another language entirely.

@rattrayalex
Copy link
Contributor Author

Ah, gotcha. Apologies.

With Flow, it's really more like type hints -- they're stripped out entirely by the time they hit runtime, even in development.

@objectkit
Copy link

objectkit commented Aug 1, 2016

Type annotations could possibly be accommodated by decorators?

class Unit
    @type String
    setId: (value) ->

That way soft typing and hard typing could be toggled just by switching the behaviour of the decorator? The information is still there in the code, and easy to see, communicating intent. The decorator implementation used during testing and development could do strict type checking, and switched to a mirror image placeholder decorator at run time in shipped code which would do no type checking...

@rattrayalex
Copy link
Contributor Author

rattrayalex commented Aug 3, 2016

@objectkit see the discussion on decorators: #9
(tl;dr, they are unlikely to make it in)

EDIT: whoops, you're active on that thread 😅

In any case, @objectkit – have you investigated Flow at all? Any drawbacks to that system that you see?

@objectkit
Copy link

objectkit commented Aug 4, 2016

@rattrayalex Hey - I have to be honest, I think theres a bit of confusion going on - it could be my own - so just to clarify:

  • Decorators would mean no need for java like annotations
  • Decorators could be used to assert parameter and return types without type annotations

But with that said, opt in type annotations in CS is a good idea. I took a look at Flow, and it seems to have the right attitude, but would incur the introduction of a new syntax into the next CS

With gradual type annotations, this

fun = (name, value) ->
            # impl

could be expressed as (something like) this

fun = (String name, Number value) -> : Result
            # impl

Assuming I'm still on the same page, if a new syntax were introduced, to support a gradualistic approach to their introduction would introduce compilation overhead (e.g. when to discard, when to enforce by introducing type checking code in transpiled code). IMO, at certain stages of development, implicit run time type checking is a very good thing! But the only way I can see getting the best of both worlds is by decorators. That way, strict decorators could be imported during testing and development, placeholder decorators could ship with output code once tested.

@type String
@type Number
@returns Result
fun = (name, value) ->
            # impl

or

@params String, Number
@returns Result
fun = (name, value) ->
            # impl

Using decorators in place of type annotations could work, intent is semantically clear (apart from the ampersand right now!), gives developers liberty to opt-in or opt-out of typing, and they can always roll their own decorators, or use a decorator library.... I think decorators would go with the spirit of CS in context of enforcing type safety. KISS, but at times, be picky! The advantage of syntax over decorators though is enforcement at compile time, not run time (when decorators kick in)...

@rattrayalex
Copy link
Contributor Author

Static types are great for a lot of reasons, only one of which is "check that this kind of bug won't happen". They also give the editor a lot of useful hints about the program; see the gifs on https://atom.io/packages/atom-typescript for some examples of what's possible.

Decorator-provided types have been an option for a long time, and yet nobody's opted to build a (widely used) library that provides them, so I'm going to assume that's not an option people find valuable. On the other hand, developers have been flocking to TypeScript and Flow, and dynamic languages like Python are introducing static typing through projects like MyPy and Pep484.

Let's keep this conversation focused on static types, if that's okay. Runtime types can always be done in an outside library, and provide a different set of benefits.

@JimPanic
Copy link
Contributor

JimPanic commented Aug 5, 2016

I agree. I wonder how much of that static analysis shown in the gif relies on TypeScript's actual types. It seems from the snippet at hand this very example of reassigning different types could well be part of the CS compilation process. Might not even need annotations for that?

@rattrayalex
Copy link
Contributor Author

I really recommend anyone on this thread learn more about static typing, TypeScript and Flow specifically, and try the languages out for a bit.

@JimPanic I think you're talking about Type Inference. Flow does a lot of inference – almost as much as it can – but there are definitely times when that's not possible.

@objectkit
Copy link

@rattrayalex I understand static typing- I was simply trying to communicate an alternative way to accommodate the use of type enforcement in the language at run time in a way that doesn't significantly alter CS- I totally agree with the merit of static typing at compile time - if it won't compile, it should be evident and an IDE should report it. Run time bugs in a language host executing the code can be a right PIA and can happen with transpiled ES, even if CS compilation is successful. I'm personally a fan of types, and especially when they can be introduced gradually - e.g. turning a quick proof of concept code into a robust formal expression of the code when needed. I remember ActionScript 1,2 and 3 succession and its commitment to ECMAScript. And even Microsofts attempt at typed JS all the way back in the early 00's before TypeScript. A language that is flexible enough to transition from sketch to formalism definitely has its benefits, and I really hope this language goes in that direction- to be able to write CS 1 to begin, then to formalise it with types in the next edition. That would hopefully make transition possible from existing CS code to this. It's tricky. Could we attempt working on a new general purpose parser / lexer / compiler for CS first, starting with an independent grammar definition of CS, then once succeed in an alternative CS compiler, formalise a new edition of it? That approach may work? I suspect the workflow of designing the language being distinct from the implementation of the compiler would have lots of benefits and give a concrete goal. First release should compile CS 1.10.0, next release will compile the next agreed language edition, with hopefully minimal impact on the implementation of the compiler. A multistage workflow? I think I can see why you were considering Babel...

@zeekay
Copy link

zeekay commented Aug 5, 2016

I'd love optional typing ala TypeScript. In terms of syntax I've always been fond of the Haskell-inspired syntax used by contracts.coffee.

id :: (Num) -> Num
id = (x) -> x

@mrmowgli
Copy link

mrmowgli commented Aug 20, 2016

I am wondering if it would make sense to create an alternate comment type, that implied pre-processing or optional hinting?

In other words I like the idea mentioned by @JimPanic with Elixr, but I don't like the @ symbol reuse. I also expect there would be a number of cases where we would want to hint code or apply a preprocessor, and really this should be ignored by previous versions of coffeescript. Adding a comment modifier would let both happen. For example #- used with a type hint:

#-spec (number, number) : String
(x, y)->
  # Normal comment
  "delta: #{y - x}" 

@rattrayalex
Copy link
Contributor Author

Closing as this has been marked as "no action" in https://github.com/coffeescript6/discuss/blob/master/Features.md

I'm personally not opposed to re-opening in the future.

@rattrayalex
Copy link
Contributor Author

Appreciate the followup @dadleyy !

I agree with @GeoffreyBooth that an officially-supported/documented way of using Flow through type comments is probably the best way forward. Though I haven't yet seen an example of that actually being done successfully, so I'm not sure if it's as simple as it sounds.

@GeoffreyBooth
Copy link
Collaborator

@rattrayalex why don’t we reopen this issue, and it becomes (at least) one of documentation? Where the task is for someone to write a guide on how to use type checking via comments and Flow or similar tools.

And if as part of that guide we find that there’s something the compiler can do to make that process work better, that can spin off into its own effort.

@rattrayalex
Copy link
Contributor Author

Sounds good!

@rattrayalex rattrayalex reopened this Dec 17, 2016
@ArmorDarks
Copy link

I'm surprised how much resistance got type annotation in the beginning. Though, I'm glad that to the end of the thread people started to change their position.

It would be great to have type annotation in CS.

Regarding decorators: they aren't full solution to this issue. First of all, it is still unclear will decorators make their way into JS at all (since they are still only a proposal with experimental implementations via babel), but even if they will appear in JS and CS, decorators results in runtime type checking with a lot of limitations, and, as already mentioned above, in most cases you want to have static type checking. Also, not to mention, that type checking with decorators could be implemented as external library, so there is no reason to make it part of CS core.

Type annotation through comments seems to be an option, but I think not all people will be happy with it. Though, I'm not opposed to have this option too.

Btw, Flow now supports annotation with comments too, so there is no need to use flotate anymore. See here. Did someone try it? Maybe it's possible to make it work with CS?

And also this seems to be possible with Google Closure Compiler too. See example here.

Looking on this from another perspective, since we already have Flow and TypeScript, maybe type checking shouldn't be responsibility of CS indeed. After all, we already have tools for it. But the issue here is that so far it's impossible to use type annotation in CS and pass the result to Flow or TypeScript for type checking.

@mrmowgli
Copy link

mrmowgli commented Mar 19, 2017

Ok I looked at the flow comment integration mentioned by @ArmorDarks, Flow typing in comments. Should work. Has anyone successfully used this in a project?

@ArmorDarks
Copy link

I personally didn't try, and probably won't, because we're slowly moving away from CS to ES6.

I think this won't be an issue at all if CS could be transpiled through Babel. Really, this is one of reason we're leaving CS — it is so damn hard to integrate into nowadays JS pipelines.

I also must correct myself. I said:

but even if they will appear in JS and CS, decorators results in runtime type checking with a lot of limitations

That's not true. In fact, runtime type checking is quite cool and allows to do a lot of nice things, like checking against schema incoming through AJAX data, etc. This something that is impossible with static type checking, because it's done during compilation and unavailable in runtime.

But in many cases still you'd like to have static type checking, or even both.

We've made small research about available popular solutions, you can read results here: https://github.com/LotusTM/Kotsu/issues/165

If anyone is okay with runtime type checking, I can't recommend more tcomb. It is compact and elegant lib with very powerful API for checking types in runtime.

@mrmowgli
Copy link

Interesting :) I'll check those out! The annotation thing I think could be handled very nicely with functional programming styles like in this thread. I imagine those of us that are going to stick with CS are going to want to try Flow or find some ways to bring tools like tcomb in.

Hopefully I'll get a chance to see if Flow will work well in CS, or someone will tell us how it worked for them!

@ArmorDarks
Copy link

ArmorDarks commented Mar 19, 2017

Interesting :) I'll check those out! The annotation thing I think could be handled very nicely with functional programming styles like in this thread

It is possible to write wrapper around, let's say, tcomb which will simply pass values to decorators. Though, I'm not sure it worth it.

find some ways to bring tools like tcomb in.

Any runtime type checker like tcomb can be used in CS as it is right now, since, after all, they just provides typical functions and do not require transpilation.

For example, with tcomb:

t = require 'tcomb';

sum = (a, b) ->
  t.Number a
  t.Number b
  a + b

sum(1, 's') # throws '[tcomb] Invalid value "s" supplied to Number'

In fact, syntax somehow alike to decorators.

But yeah, with Flow it is completely different story...

@rattrayalex
Copy link
Contributor Author

The difficulty of supporting flow – even through comments, which seems pretty unergonomic – is a major part of the reason why I built LightScript, which recently hit npm for beta use.

LightScript doesn't plug into the flow typechecker yet (planning stages), but it supports the flow type syntax and tcomb, I think pretty smoothly. There's also basic support for ESLint.

I'd welcome feedback from anyone willing to give it a try, and would love help in building the flow typechecker integration if anyone's up for the task.

@mrmowgli
Copy link

I think either way this needs to be addressed in CoffeeScript, so I'll keep digging.

@zeekay
Copy link

zeekay commented Mar 22, 2017

I'd love to see the CoffeeScript syntax extended to support type annotations (think Python's typing module).

Ideally I think these type annotations would extend the CoffeeScript AST, which could be used by an adapter for any given type checker. We'd probably throw them out when outputting JS, although runtime checking ala contracts.coffee would be pretty neat.

A unified set of type annotations which could be adapted into the appropriate type checker would prevent us from getting locked-in to a specific type checking tool and simplify experimenting with different type checkers.

@GeoffreyBooth
Copy link
Collaborator

GeoffreyBooth commented Jul 25, 2017

This is possible now in jashkenas/coffeescript#4572. See documentation for how to use comments with Flow for static type checking.

If you’d like to try it out, I’d love to know how well it works (or doesn’t) for you!

@GeoffreyBooth
Copy link
Collaborator

Implemented Flow comments syntax via jashkenas/coffeescript#4572. If people would like to propose alternative implementations for static type checking, please open new issues.

@GeoffreyBooth
Copy link
Collaborator

Hey folks, I’m looking for testers for jashkenas/coffeescript#4753. That PR should allow using all parts of Flow’s comments-based syntax via CoffeeScript comments. If anyone has a current CoffeeScript project they’d like to add Flow comments to, or a current JS Flow project they’d like to convert to CoffeeScript with the Flow types in comments, that would be a great way to test this.

Also, if someone can create a single .coffee file that’s a kitchen sink demonstration of every part of the Flow syntax, that would be a great file to add to our tests.

@coffeescriptbot
Copy link
Collaborator

Migrated to jashkenas/coffeescript#4918

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests