Closed simonexmachina closed 8 years ago
@bjmiller:
ES6 modules are kind of terrible
I'd like to hear more on why you feel this way. I personally love the ES6 modules. Remember, their main benefit over CommonJS is that they may be statically determined, so tools like Browserify and my own CommonJS Everywhere can actually do what they claim to be able to do. And unlike other terrible module systems from other languages, they don't magically bring any names into scope. I think their design is quite elegant and obviously very carefully thought out.
By the way, I totally agree that generators and TCO are by far the most important features coming out of ES6.
@bjmiller Those are interesting thoughts. I agree that you certainly shouldn't have to use ES6 to use coffeescript.
However, incorporating the changes for ES6 would probably be significant enough to warrant a major version bump - CS2.x.x - so you would be able to continue to use es5-only coffeescript if you preferred.
As for whether coffeescript should support ES6 at all, I honestly think the question of es6's design quality is irrelevant. It's the future of JavaScript by fiat. Just like JavaScript is the language of the web by fiat, regardless of its strength as a language.
If ES6 has warts, let's do for them what @jashkenas did for ES3.
At least the tail calls feature does not introduce new syntax.
@michaelficarra: I took a little time to look more closely at ES6 (Or, is it ES2015?) modules.
I find that it's harder to reason about, and that maybe it's trying to solve too many problems at once?
This helped me a little: https://github.com/addyosmani/es6-equivalents-in-es5#modules, but I'm even more confused now about what "default" is supposed to do.
This also gave me some concerns: http://www.geedew.com/2014/12/26/es6-module-gotchas/
Also of note: No one is anywhere near a "real" implementation yet. It seems like the "6to5" project has the best support so far.
Lastly, I actually like the fact that you can require things dynamically. I get a lot of use out of that. But, I can see how that can be frustrating for implementors.
Friends, I don't think that our personal feelings on ES6 module are relevant to this discussion. What I would like to walk away with is concrete understand where CoffeeScript stands in relation to ES6.
As a long-time CS user I had to migrate to LiveScript more than a year ago because Generators became important for my team and we needed something that wasn't stuck in ES3 land. I'm overjoyed to hear that CS will have support for generators in its next version, when is this planned for release?
Yesterday ;-) http://coffeescript.org/#changelog
On Fri Jan 30 2015 at 6:37:16 PM Sleepyfox notifications@github.com wrote:
As a long-time CS user I had to migrate to LiveScript more than a year ago because Generators became important for my team and we needed something that wasn't stuck in ES3 land. I'm overjoyed to hear that CS will have support for generators in its next version, when is this planned for release?
— Reply to this email directly or view it on GitHub https://github.com/jashkenas/coffeescript/issues/3162#issuecomment-72197929 .
I agree that CS still does more than a few things better than even ES6 JS and I don't really have any plans to stop using in in favour of ES6 JS. However a couple of features stand out to me as things I'd like to have in CS. Namely destructuring and getters and setters.
destructuring
CoffeeScript has got that.
Getters and setters, however, have be turned down already.
I strongly suggest you look at Crockford's 'module pattern' as a better solution than exposing an object's attributes externally.
I'm working on a 9.5MB CoffeeScript source project at my day job and am struggling to convince the team to stay on CoffeeScript... Don't throw around silly suggestions about Crockford's patterns, that's very narrow minded.
We are fully committed to CS, but looking even couple of years down the road it seems be mostly obsoleted by ES6 and not to say completely stalled. I can't make a valid argument against rewriting the app in ES6 no matter how I try... Which is very upsetting to me personally since I have almost 4 years of full time dev experience in it and still love it.
While I personally like CommonJS require + Browserify a lot better, it's becoming obvious that the ability to use the import
, default
, and export
keywords is something that people really want.
Can they be removed from the CS "reserved word" list? I think that (plus an additional transpiler layer of the user's choice) is enough to solve the problem for people.
@alexgorbatchev: You should make the case to your team that nearly every ES6 feature that they want is already done better by CoffeeScript today.
Can we avoid calling each other silly and childish?
I don't think anyone struggling to convince their team about CS over ES6 has forgotten to mention that it has virtually all the same features, only better. That clearly isn't enough.
We don't decide the EcmaScript standard that coffeescript targets. Ecma International does. They have (all but) decided on the future of JavaScript. Why wouldn't we target that?
On Wed, Feb 4, 2015, 7:09 AM Brian Miller notifications@github.com wrote:
While I personally like CommonJS require + Browserify a lot better, it's becoming obvious that the ability to use the import, default, and export keywords is something that people really want.
Can they be removed from the CS "reserved word" list? I think that (plus an additional transpiler layer of the user's choice) is enough to solve the problem for people.
@alexgorbatchev https://github.com/alexgorbatchev: You should make the case to your team that nearly every ES6 feature that they want is already done better by CoffeeScript today.
— Reply to this email directly or view it on GitHub https://github.com/jashkenas/coffeescript/issues/3162#issuecomment-72774383 .
My team had a simple answer: one more thing to learn! They were already learning about GIS, Agent Based Modeling and more technologies and learning CS was overwhelming.
Not that I agree, but just an insight into the team difficulties mentioned by others.
On Tue, Feb 3, 2015 at 8:50 PM, Alex Rattray notifications@github.com wrote:
Can we avoid calling each other silly and childish?
I don't think anyone struggling to convince their team about CS over ES6 has forgotten to mention that it has virtually all the same features, only better. That clearly isn't enough.
We don't decide the EcmaScript standard that coffeescript targets. Ecma International does. They have (all but) decided on the future of JavaScript. Why wouldn't we target that?
On Wed, Feb 4, 2015, 7:09 AM Brian Miller notifications@github.com wrote:
While I personally like CommonJS require + Browserify a lot better, it's becoming obvious that the ability to use the import, default, and export keywords is something that people really want.
Can they be removed from the CS "reserved word" list? I think that (plus an additional transpiler layer of the user's choice) is enough to solve the problem for people.
@alexgorbatchev https://github.com/alexgorbatchev: You should make the case to your team that nearly every ES6 feature that they want is already done better by CoffeeScript today.
— Reply to this email directly or view it on GitHub < https://github.com/jashkenas/coffeescript/issues/3162#issuecomment-72774383
.
— Reply to this email directly or view it on GitHub https://github.com/jashkenas/coffeescript/issues/3162#issuecomment-72786033 .
If someone wants to cook up a solid, thought-through proposal that adds module-related keywords to CoffeeScript (e.g. export
), and allows the resulting output to compile down to either ES2015 modules, CommonJS modules, or AMD modules — as specified by the user, I think that would be a proposal worth taking a look at.
I think module system transpilation (es6 -> commonjs / amd) is outside the scope of coffeescript, whose sole purpose is to produce javascript.
That said, no reason not to allow users to use the import / export keywords if they're targeting es6.
I'm happy that @jashkenas finally expressed interest in implementing modules.
But u agree with @kieran: CS modules should taerget modern ES standard. It's up to user whether not to use them, use them literally, or use with a transpiler.
Recommendation: Ask Guy Bedford about how to do this, he has the most experience, I believe, in modules and optimal use of them for both node and the browser.
On Wed, Feb 4, 2015 at 9:36 AM, Andrey Mikhaylov (lolmaus) < notifications@github.com> wrote:
I'm happy that @jashkenas https://github.com/jashkenas finally expressed interest in implementing modules.
But u agree with @kieran https://github.com/kieran: CS modules should taerget modern ES standard. It's up to user whether not to use them, use them literally, or use with a transpiler.
— Reply to this email directly or view it on GitHub https://github.com/jashkenas/coffeescript/issues/3162#issuecomment-72888042 .
I don't want to go that far. I believe that "supporting" ES6 modules should require nothing more than moving import
, export
, and default
from the "forbidden" list to the "strict proscribed" list. This should allow anyone to use those keywords, and have them show up in the compiled code the way they're written in CS.
After that, another preprocessor can take it from there. Eventually, there might be browser support, too. But, we don't need to get involved in that. And, we shouldn't.
On the other hand, if anyone wants to write and maintain an independent translator between module formats, I imagine that would go over really well with the community.
I agree with @bjmiller. I would not expect that CoffeeScript would implement those keywords. Making those words no longer keywords would be a good start.
After looking at the proposed syntax, there's a slight additional tweak necessary to my suggestion. If you write import { a, b } from 'foo';
, the bracketed expression would expand into an object. So, you'd have to either be aware to not do that, or have an alternate syntax that ends up correct in the end.
Any suggestions on a proper CoffeeScript-y way to handle that?
Maybe the same rules that cover backticks could be applied to the pairs import ... \n
and export ... \n
?
No, we just need a different kind of "objects" that won't expand {a}
to {a: a}
(or maybe we can do away with it entirely and just have an import node)
And we obviously need the lexer to be taught not to treat imports as implicit calls.
Well... it looks like this will take more work than just permitting previously forbidden keywords. :weary:
Well... it looks like this will take more work than just permitting previously forbidden keywords.
Yes, that would be a lazy and half-assed way to go about it ;)
CoffeeScript needs to be a coherent language — we should be putting in keywords that we "ignore" and hope that some other transpiler in the pipeline will be able to take care of.
If we're going to support export
, then we should support it. Given that ES6 (ES2015) modules still don't exist at all (http://kangax.github.io/compat-table/es6/) ... that would mean compiling down to the different popular module formats that do.
I think that such a pull request has a moderately slim chance of being accepted — it's probably still better to wait until real JS modules are finished and fully described. But it's worth a shot.
It might be time to revisit this, a proper module system is one of the strongest reasons people are adopting Babel / abandoning coffeescript.
Rather than leaving export/import statements in the compiled source and leaving the job to another project like Webpack isn't it possible to wrap any files with an import statement and delaying them until the dependencies are met to make them load order independent?
Here's a naive version of what I'm describing:
lib.coffee
export square(x)-> x * x
script.coffee
import { square } from 'lib'
alert square(2)
lib.js
// head
var _modules, _scripts;
_modules = {};
_scripts = [];
(function() {
_modules['lib'] = {}
_modules['lib'].square = function(x) {
return x * x;
}
})();
script.js
_scripts.push(function() {
var square;
square = _modules['lib'].square;
alert(square(2));
})
// tail
var i, len;
for (var i = 0, len = _scripts.length; i < len; i++) {
_scripts[i]();
}
This may be naive but it doesn't seem like an impossible task to get the expected behavior of modules inside of coffeescript.
The issue is module loaders, which are not really part of es6. JSPM is the closest to the proposed standard.
But this would require runtime support.
A solution would be to have a workflow that bundles the modules. JSPM does provide for that. WebPack and Browserify can do this too I think.
Your suggestion requires the compiler to know in advance what the other modules are, or whether they even exist, and then resolving the dependencies.
This would require at least as much code as already comprises CS, and is clearly the job for another project.
The only thing that CS should do here is enable the user to specify the syntax for modules that they want. This already works for commonjs and AMD. The only thing CS can't do is emit the unpleasant ES6 module format without using backticks. There should be a way to do simply that - but no more.
But this would require runtime support.
Yeah, I was proposing an alternative that could compile to javascript without modules or need for a runtime. What are the important features of modules that you'd lose if you stripped them out?
I know the original question is about being able to consume es6 modules within coffeescript, my solution doesn't help with that at all, it would only allow us to use export / import in coffeescript. If you're planning on mixing es6 and coffeescript then you'd definitely need a module loader and runtime.
Your suggestion requires the compiler to know in advance what the other modules are, or whether they even exist, and then resolving the dependencies.
Would it? Why is the current filepath not enough? You'd need to include a head / tail script at some point. It will throw an error if you try to import something that doesn't exist but that's exactly what I'd expect.
I've been using poor mans modules/namespaces and it's been working well except for the problem of order dependency.
# pseudo export
App.Components.Tab = {}
# pseudo import
{ Tab } = App.Components
Now that es6 has widespread adoption I'd prefer to be able to use export / import being aware of the filepath as the module-name and have the issue of source order independence sorted. My silly example delays any scripts using an import til the end and runs through them in order.
The idea of modules is being able to share and consume code in standard ways, so limiting coffeescript modules to use within coffeescript is probably a terrible idea. Move along.
BTW: I did a successful experiment using backticks
with module
import/export within coffeescript "modules", first compiling w/
coffeescript, then running the resulting JS through traceur with only
modules enabled. Then using a module loader to manage the difficulties of
actually gluing the results together. It worked fine, although adds an
extra workflow step.
On Tue, Oct 6, 2015 at 7:00 PM, Mark notifications@github.com wrote:
But this would require runtime support. Yeah, I was proposing an alternative that could compile to javascript without modules or need for a runtime. What are the important features of modules that you'd lose if you stripped them out?
Your suggestion requires the compiler to know in advance what the other modules are, or whether they even exist, and then resolving the dependencies. Would it? Why is the current filepath not enough? You'd need to include a head / tail script at some point. It will throw an error if you try to import something that doesn't exist but that's exactly what I'd expect.
I've been using poor mans modules/namespaces and it's been working well except for the problem of order dependency.
pseudo exportApp.Components.Tab = {}
pseudo import{ Tab } = App.Components
Now that es6 has widespread adoption I'd prefer to be able to use export / import being aware of the filepath as the module-name and have the order of source order independence sorted. My silly example delays any scripts using an import til the end and runs through them in order.
— Reply to this email directly or view it on GitHub https://github.com/jashkenas/coffeescript/issues/3162#issuecomment-146047060 .
I'm about to abandon CoffeeScript because non-compatibility with ES6 style modules. The lack of import/export for more than two years seems like a sort of language suicide. A shame because I really loved CoffeeScript and will hate to walk away from it.
I'm in the same boat, I think a lot of others are as well. It's hard to keep up with new libraries and techniques when they require huge workarounds to function with CoffeeScript.
For modules, I recommend JSPM .. its more standards based.
I'm using it because for development, it has no workflow .. just code and load in a browser.
I've also used the JSPM bundler and it does just what it should, and produces very fast code. It is built on top of es6-module-loader (github) if you'd prefer more control over module loading. Both are lead by Guy Bedford.
I recommend Babel over Traceur due to readability and wider scope such as JSX etc, and es7 support too.
The main problem for modules is workflow for module loading (not part of es6 spec), which is what JSPM is, although it will transpile while traversing the dependencies too.
But there are way to many, legacy approaches like bower, browserify, webpack and so on. Yes they work but I dislike the multiple formats being produced so I'm sticking with the standards approach JSPM and es6-module-loader via System.js.
-- Owen
On Sun, Oct 25, 2015 at 1:18 PM, Sean Roberts notifications@github.com wrote:
I'm in the same boat, I think a lot of others are as well. It's hard to keep up with new libraries and techniques when they require huge workarounds to function with CoffeeScript.
— Reply to this email directly or view it on GitHub https://github.com/jashkenas/coffeescript/issues/3162#issuecomment-150960128 .
Why can't import
/export
be passed through like require
is? CoffeeScript doesn't resolve require
statements, it lets Node or Browserify handle it. Why can't import
and export
be the same way?
require
is just a regular function. Nothing special is done about it. import
and export
are language constructs.
Yes - it is because they are so different from legal ES5 constructs that there's no way to express them using CS.
I'll reiterate that I personally think that the syntax should change slightly to allow valid ES6 module statements to pass through untouched without the use of backticks, but that nothing more should be done with them. This would make everyone happy, while being fully backwards-compatible - since export
, import
, and default
are all presently on the reserved words list.
I think the solution is now clear, along the lines of what Jeremy originally suggested and what @bjmiller is saying, all it needs is someone to bang out the implementation, make a fork, test it, and it'll come around to the compiler. It's not gonna be simple, because it requires treating syntax after import
and export
differently, but it's doable.
Some notes (following the description here, if it's incorrect correct me):
export { each as foreach }
should (imo) be export {foreach: each}
import { square, diag } from 'lib'
could be used verbatim, although it is a rather strange new piece of syntax to add to CoffeeScript (esthetically, passing a string to from
feels really weird), I think Node makes so much more sense here, but surely the committee had their reasons..., I can only think of import 'lib' as {square, diag}
, {square, diag} = import 'lib'
is probably undoable and has too much of a surprising compilation output
import _, { each } from 'underscore'
(verbatim)import 'underscore' as _, { each }
(reversed)_ = {each} = import 'underscore'
(tough to parse)import myFunc from 'myFunc'
) (although see below){a: b}
instead of {b as a}
in import
import * as mylib from 'src/mylib'
, which is the object import, different from the default import (for some reason there are both), again, we can implement it verbatim or, for example:
default
key, import 'myFunc' as {default: myFunc}
, leaving import 'src/mylib' as mylib
or mylib = import 'src/mylib'
as the object import aboveFolks here will surely have a better sense of what to do syntactically. The output is a valid ES6 code, intended to be further compiled using Babel or whatever you prefer.
I don't think we even have to do that much. Just make the syntax for import and export legal (as it is in ES6), and have it place the exact statements as written to the compiled result.
I don't believe that there is anything that CS can do to make the syntax any more palatable. I've tried thinking about how one could make the syntax of it less ugly, and nothing that comes to mind improves it any. ¯(ツ)/¯
But, ultimately, the use of that syntax is what people are asking for. No one wants an alternate version of module salad. They just want to pass the output to another tool in the chain. I see this as being somewhat like generators, in that it's a "buyer beware" feature. You use it if you know you can get it to work downstream, otherwise you avoid it.
What I definitely don't want to see is an attempt at also doing what Babel does, changing ES6 module syntax to CommonJS require statements.
With the upcoming release of Meteor 1.3, we will have two major JavaScript frameworks (along with Ember) that require import
/export
statements. I think it’s time to prioritize allowing a way, other than backticks, to pass through import
and export
for an ES2015 parser to evaluate.
ember-cli-coffees6 works around this problem by doing some string substitutions to replace import
/export
statements written in Coffee style before the real CoffeeScript compiler parses the code. This strikes me as a hack for something that should be part of CoffeeScript core, but I thought I’d draw your attention to one possible solution.
One other thought: should we not add something like target: 'es2015'
to the build params? That way ES2015 import
/export
statements are allowed only when CoffeeScript is configured to output ES2015, rather than ES5; and when the target is ES5 either keep the current lack of support for import
/export
or implement one of the other proposals mentioned above. (This also opens the door for other variations in generated output for target: 'es2015'
mode, such as compiling =>
into =>
, for example.)
@GeoffreyBooth Agree with the proposal for the build targets and the reason.
I also believe that this is a major feature hole in CoffeeScript and to keep it relevant in relation to the current alternatives (es6, TypeScript, etc...) the ability to have imports/exports needs to be present. I've been having to hack in solutions for large projects in the meantime. This is an area that is pushing developers into other solutions. That's unfortunate because CoffeeScript has a lot to offer, but is becoming irrelevant by not keeping up with some of these important features.
Agreeing with @GeoffreyBooth again, this standard should be compile differently depending on it's intended target (es2015/es6). I also think the feature should look more like python imports/exports. Treat anything in the global scope of a CoffeeScript file as an export. So as a proposed example of how this could implemented:
# common.coffee
global_var = 'SOMETHING'
class SomeService
constructor: ->
doSomething: ->
helper = ->
# main.coffee
import common
service = new common.SomeService()
common.helper()
console.log common.global_var
# or
from common import SomeService, helper
service = new SomeService()
helper()
# common.helper and common.global_var are available
Also provide the ability to alias import with as like import common as shared
or from common import SomeService as SomeSharedObject, helper as someHelper
. Reason for this is that I believe it follows better the flow of CoffeeScript, and allows for it be more Pythonesque with syntactic sugar and easy readability. Which are the some of the advantages of using CoffeeScript.
@jashkenas Please reopen.
@jashkenas why is this still closed? I think the original reason for closing it is demonstrably not the case anymore.
Everything functionally works 100% fine right now AFAICT, it's just really annoying to have to add backticks to every expression that begins with import
or export
(which is becoming a major PITA)
What I'd love to see is this:
`import { member as alias } from "module-name"`
obj =
method: alias.bob
`export { obj as default }`
becoming this (no backticks, that is all):
import { member as alias } from "module-name"
obj =
method: alias.bob
export { obj as default }
FYI: the first example currently compiles (correctly) to:
import { member as alias } from "module-name";
var obj;
obj = {
method: alias.bob
};
export { obj as default };
CoffeeScript needs no knowledge of the effects of this code IMO, and targeting other module systems should be left to a transpiler after the CS compilation step.
CS is protected from compatibility concerns since import and export are already reserved words. Is there no way to simply pass through any expression that begins with them un-molested?
I believe the reason it is closed is that it is not a language issue, but a browser, WHATWG standards body, problem. Although @jashkenas could solve the strictly syntax/semantics of import/export, he could not solve the module loader problem.
From the discussion thus far, I would say many of us are not familiar just how gawd awful this is!
Please see this for a discussion on the simplest solution yet: https://groups.google.com/d/msg/systemjs/a7vB2YmdXp8/HCh1Rx0XEAAJ
I will warn you that the babel folks as well as the module loader folks will lead you down horrible workflows with npm/browserify/babelfy, webpack, and the less awful jspm. The whatwg lack of standard is creating module wars, not pleasant. Do recall that systemjs is the only standards based solution at this point in time.
I believe the issue should remain closed until we do serious research on the module loader nightmare. Then pick a strategy. And remember, your choice of loader will affect the compiled code depending on your chose of CommonJS, AMD, SystemJS.
I'll whisper on other solution: bundling. I do NOT recommend it (it is deprecated due to slowing down HTTP/2 parallel/async download speeds) but it may provide a solution to the complexity. As an example, JSPM provides a bundling method with no additional required Githubissues.
Maybe I'm missing something, but I can't see how to use ES6 modules from CoffeeScript. We should have this.