Closed coffeescriptbot closed 6 years ago
From @JimPanic on 2016-09-01 16:06
Good catch! This is definitely something that needs work.
Would it make sense to replace the current literal-JS syntax? Or should we rather introduce a different syntax for template strings?
From @lydell on 2016-09-01 16:07
See also: https://github.com/jashkenas/coffeescript/issues/1504#issuecomment-187814583
From @greghuc on 2016-09-01 16:13
I also submitted a bug report for this on the Coffeescript repo: https://github.com/jashkenas/coffeescript/issues/4301
From @greghuc on 2016-09-06 07:30
FYI, I got tagged template literals working in my browser-side Coffeescript project by encapsulating use of this es6 functionality in its own javascript class. So - for the first time - my project contains both coffeescript and javascript files, which are all brought together in a browserify + broccoli build step.
# views/test-view.js:
module.exports = function (bel) {
var TestView = function () {
...
};
TestView.prototype.render = function (name) {
return bel`<div>Hello ${name}</div>`;
};
return TestView;
};
# And in Coffeescript-land:
bel = require 'bel'
TestView = require('views/test-view')(bel)
element = (new TestView()).render 'John'
From @rattrayalex on 2016-09-10 15:40
CoffeeScript interpolates with "
instead of ```.
Currently, code such as someTag"hello #{world}"
throws a syntax error in coffeescript, so presumably this syntax could be supported (how hard it would be, I'm not yet sure).
@greghuc is that the feature you're suggesting we build, or do I misunderstand?
From @greghuc on 2016-09-10 16:04
@rattrayalex yes, I'm imagining that someTag"hello #{world}"
should eventually work Coffeescript. That, or there needs to be an alternative to escaping Javascript using backticks, so that this doesn't break:
`tag`Hello ${ a + b } world ${ a * b }`;`
You could argue that alternative escaping is the better solution, given escaping is the root cause of the break.
I haven't done a deep dive on this, but the issue is really with ES6 tagged template literals, not template literals. ES6 template literals are equivalent to CS string interpolation. But tagged template literals are a new kind of beast. They involve all 'arguments' to the interpolated string being passed to the supplied function, which then processes them. Example from MDN:
var a = 5;
var b = 10;
function tag(strings, ...values) {
console.log(strings[0]); // "Hello "
console.log(strings[1]); // " world "
console.log(strings[2]); // ""
console.log(values[0]); // 15
console.log(values[1]); // 50
return "Bazinga!";
}
tag`Hello ${ a + b } world ${ a * b }`;
// "Bazinga!"
Might be worth backing away from this, and treating this as an issue with Javascript escaping in Coffeescript.
From @rattrayalex on 2016-09-10 17:14
Sorry, what does this have to do with escaping? Is it just a question of the syntax? I don't see why use of the backtick is required for this feature.
From @greghuc on 2016-09-10 17:25
To restate, it is currently not possible to use ES6 template literals or tagged template literals inside Coffeescript (in an escaped javascript block). This is because ES6 template laterals use backticks in their syntax, and Coffeescript already uses backticks to define the start and end of the javascript block. As such, the Coffeescript compiler breaks.
So see this, copy and paste the following into "Coffee to JS" http://js2coffee.thomaskalka.de, and view the error:
`
tag`Hello ${ a + b } world ${ a * b }`;
`
So current Coffeescript is broken for use of this ES6 feature.
From @rattrayalex on 2016-09-10 17:29
Ah, gotcha. So there are two things we could do (non-exclusively):
myTag"hello #{'wo'+'rld'}"
They both sound fairly straightforward to me, though I don't know enough about the relevant parts of the coffeescript compiler to say.
From @GeoffreyBooth on 2016-09-10 23:22
The escaping thing is just an outright bug in CoffeeScript, that’s been open since 2011: https://github.com/jashkenas/coffeescript/issues/1504 But there’s more interest in it lately since the advent of template literals. Basically it seems like there’s a consensus desire to be able to escape backticks, as well as add a triple-backtick delimiter for backticked blocks. That seems like a worthy improvement.
But fixing escaped backticks just enables a hacky workaround to using tagged template literals; it’s a far cry from supporting the feature itself in CoffeeScript syntax. Theoretically, the efforts could proceed in parallel; we could build support for myTag"hello #{'wo'+'rld'}"
without necessarily fixing backticks.
@greghuc, would you mind posting a proposal for what the CoffeeScript syntax should be for tagged template literals? Including what the expected JavaScript output would be. Would we support tagged template literal blocks, e.g. myTag"""some multiline string...
?
Also, would you like to take the lead in implementing this feature in the current compiler? It’s one of only three items in the Top Priority tier of our features list, as the only ES2015+ features we’ve found so far that imperil interoperability.
From @greghuc on 2016-09-11 10:53
@GeoffreyBooth I'll start off by posting a proposal for tagged template literals. I'll aim to have this done by next weekend (by Sunday Sept 18th). Initial thoughts:
myTag"hello #{'wo'+'rld'}"
or myTag"""some multiline string"""
Regarding taking the lead on implementing this feature, I'm hypothetically up for it. I'll make a decision once it's clearer what the scope is (once we've agreed on the feature proposal), and how much spare time I have for open-source coding.
From @GeoffreyBooth on 2016-09-12 05:01
@greghuc I agree with everything except I’m neutral on the “compiles to ‘polyfill’ JavaScript” part. I think we need to make a broader philosophical decision on whether newly-built features should still be compiling down to ES5, or should just output ESNext and leave the shimming to a tool like Babel. For modules and classes it hasn’t been an issue, since shimming modules is way beyond our capabilities and the current class
keyword is already essentially an ES5 shim; but for tagged template literals we’ll need to make a choice.
I think as a group in this repo we’ve already reached a consensus that a future version of the CoffeeScript compiler should output as much ESNext syntax as possible, leaving the shimming to other tools. I proposed a flag for enabling such output, so it can be built gradually over time and not break backward compatibility. Assuming @jashkenas signs off on such a plan, it would then be CoffeeScript’s stated goal to output modern ECMAScript whenever possible, which implies that new features we add now should just go ahead and output ESNext. Ideally they would output both, an ESNext version if the --ecmascript
flag is set and a shimmed version otherwise, if we have the time and motivation to implement both versions. But I would think that the ESNext version would be the default, since we know we need that for future versions of CoffeeScript. Building the shimmed version merely saves people from needing to attach Babel to their build chains, and I’m not sure such a savings is worth the added burden on us to build and support an alternate (much more complicated) output for a new feature.
So I guess would you be okay will building tagged template literals that output at least as ESNext? And if you want to take on the added challenge of outputting ES5, that’s awesome.
From @greghuc on 2016-09-12 06:53
@GeoffreyBooth I think outputting tagged template literals in ESNext is actually the nicest option:
I considered the polyfill approach, as I didn't think outputting ESNext would be an option. So I'd be happy aiming for ESNext output for first implementation.
But I'll start with the proposal first.
From @GeoffreyBooth on 2016-09-12 07:06
@greghuc that sounds great. I don’t think this feature even needs a flag; like generators or modules, using the new syntax opts into getting ESNext output. We only need to worry about flags if you wanted to build both possible outputs.
From @jashkenas on 2016-09-12 20:35
Assuming @jashkenas signs off on such a plan, it would then be CoffeeScript’s stated goal to output modern ECMAScript whenever possible, which implies that new features we add now should just go ahead and output ESNext.
My preferred plan would be for a CoffeeScript 2.0 to implement and output modern ECMAScript — as soon as those features are shipping in real browsers and Node. I think that to output them earlier than that point (to rely on chains of transpilers, and promises to implement the spec as its written but before its implemented) would be foolish.
From @nilskp on 2016-09-14 15:37
Triple quotes is used in a lot of languages to allow not escaping the single quote. Couldn't something be used here? I.e. triple backticks?
From @rattrayalex on 2016-09-14 16:22
@nilskp yes, see above.
From @nilskp on 2016-09-14 17:26
@rattrayalex Ah, yes. I didn't read the thread carefully enough.
From @DomVinyard on 2016-09-15 22:34
@jashkenas
to rely on chains of transpilers, and promises to implement the spec as its written but before its implemented would be foolish
Why foolish? That seems like an odd indictment of this entire effort.
From @jcollum on 2016-09-16 02:24
I think what he's saying is that CS2 shouldn't try to confirm to the spec for ES2016 before ES2016 is actually implemented in Node 7 (which would give time for the kinks to be worked out? make sure the feature will actually make it to the language?).
From @mrmowgli on 2016-09-16 03:19
You can also put such things behind a different branch, ie esnext. I get it, the idea is really about only putting things into the language that will actually be adopted. However we can "Anticipate" future features by placing it in an unstable or experimental branch.
Perhaps we should do a formal proposal for the release process.
CS2(CS3, CS4 etc.) branches would include breaking changes (for instance classes) and any currently existing (Implemented in Babel?) high priority items that are already shipping in browsers. The ESNext branch would implement soft targets or features that are nearly complete in browsers. The experimental branch would be implementations of low priority ES6/7 targets that aren't fully defined.
I also wouldn't mind having tagged "Stable" versions.
From @GeoffreyBooth on 2016-09-16 04:58
Based on some of @jashkenas’ other comments, I think that what he means by waiting for browser implementation is not that he feels that people should rely on the coffee
command-line tool as their one and only transpiler, but rather that a spec is just words on paper until browsers have implemented it. So if we support a spec before it’s widely supported, we risk supporting something that’s not really final.
Getting back to template literals, support is widespread in evergreen browsers:
So I think this feature is safe to implement. Per this comment, unless anyone objects to @jashkenas, we’re going to skip the ES3 version of template literals and go straight to ESNext.
@greghuc are you going to implement both tagged template literals and regular template literals? You’re welcome to do both if you’re up to the challenge, though if we compile "foo#{bar}"
to foo${bar}
we would need to release this update as part of a proposed 2.0.0-beta
release.
From @greghuc on 2016-09-18 17:28
As discussed, I've written up a proposal for adding ES6 'tagged template literal' support to Coffeescript. Executive summary follows, followed by more detail (code examples, etc). Opinions welcome.
Template literals and tagged template literals are a new feature in ES6:
The current situation with Coffeescript:
Proposals for Coffeescript to interoperate/adopt ES6 template literals:
Three final observations:
Executive summary ends.
ES6 template literals cannot be used in an embedded Javascript block, since the new syntax uses backticks. Backticks are already used by Coffeescript to define the start and end of the JS block. This example blows up:
`
`3 + 2 = ${3 + 2}`
`
The proposed fix is for Coffeescript to allow embedded Javascript blocks to be delineated with 3-backticks markdown-style, and not escape backticks in the Javascript block. E.g.:
`` (should be 3 backticks, but github can't handle)
`3 + 2 = ${3 + 2}`
`` (should be 3 backticks, but github can't handle)
Reasoning:
Summary of ES6 template literals
An ES6 'template literal' is a string literal that can include interpolated expressions, and can be multi-line. Example:
`3 + 2
= ${3 + 2}`
//Output string
"3 + 2
= 5"
An ES6 'tagged template literal' is a template literal, but prefixed with a function reference. The function is called to generate the output string, being supplied with the template literal in the form of expressions and the text between them. Example:
//Uppercase the given expressions
function upperExpr(text, ...expressions) {
return text.reduce((accumulator, textPart, i) => {
return accumulator + expressions[i - 1].toUpperCase() + textPart
})
}
var name = 'Greg'
var food = 'sushi'
upperExpr`Hi ${name}
Do you like ${food}?`
//Output string
"Hi GREG
Do you like SUSHI?"
So the simpler 'template literal' can be seen as a tagged template literal with an invisible 'normal' function that just concatenates the given text-parts and expressions together.
Coffeescript proposal
The proposal is for Coffeescript to 'natively' adopt tagged template literals. The implementation will augment existing Coffeescript behaviour for single-line, multi-line and block strings. As such, the implementation will slightly differ from ES6 for multi-line block-strings, due to the spacing differences.
Single-line string:
upperExpr = (text, expressions...) ->
text.reduce (accumulator, textPart, i) ->
accumulator + expressions[i - 1].toUpperCase() + textPart
name = 'Greg'
food = 'sushi'
//CS input
upperExpr"Hi #{name} Do you like #{food}?"
//ES6 output
upperExpr`Hi ${name} Do you like ${food}?`;
Multiline string:
upperExpr = (text, expressions...) ->
text.reduce (accumulator, textPart, i) ->
accumulator + expressions[i - 1].toUpperCase() + textPart
name = 'Ismael'
food = 'whales'
//CS input
upperExpr"Hi #{name}. Do
you like #{food}?"
//ES6 output
upperExpr`Hi ${name}. Do you like ${food}?`;
Block string:
upperExpr = (text, expressions...) ->
text.reduce (accumulator, textPart, i) ->
accumulator + expressions[i - 1].toUpperCase() + textPart
language = 'coffeescript'
//CS input
upperExpr"""
<strong>
cup of #{language}
</strong>
"""
//ES6 output
upperExpr`<strong>\n cup of ${language}\n</strong>`;
Coffeescript already supported multi-line strings with 'block strings'. In ES6, multi-line strings are now supported with string literals. But the two differ in terms of spacing. Example:
CS block string:
"""
<strong>
cup of coffeescript
</strong>
"""
outputs JS string of
"<strong>
cup of coffeescript
</strong>"
ES6 multi-line string literal:
`
<strong>
cup of coffeescript
</strong>
`
outputs JS string of
"
<strong>
cup of coffeescript
</strong>
"
To completely align Coffeescript string behaviour with ES6 template literals, the spacing semantics of a Coffeescript block-string would need changing to match a multi-line ES6 template literal. This change would be backwards incompatible, so has not been proposed.
I found an example where ES6 tagged template literals are nested inside one another. To support this, Coffeescript must also be able to nested interpolated strings. I did some testing, and discovered that CS cannot nest block strings. Bug?
Busted:
# Nested block strings
""" A test of #{"nesting" + func("""
quotes
""")}
inside each other
"""
Works:
"A test of #{"nesting" + func("quotes")} inside each other"
"A test of #{"nesting" + func("""
quotes
quotes
""")} inside each other"
""" A test of #{"nesting" + func("2")}
inside each other
"""
From @GeoffreyBooth on 2016-09-18 18:29
@greghuc this is heroic. In the interest of organizing our discussion, would you mind splitting some of these out into separate issues? I’m not sure template literals and tagged template literals should really be discussed separately, so we could keep those here; but the block-backticks operator could get its own issue, as could the bug about nested block strings (that should perhaps be a bug issue on the main coffeescript repo).
BTW if you want to type three backticks in a code block in GitHub, you can use its other code block syntax, which is indenting with four spaces:
console.log(I’m a JS code block embedded in CoffeeScript! The time is ${Date.now()}
);
From @lydell on 2016-09-18 18:31
A few little things here:
${}
is invalid JavaScript, while "#{}"
is valid CoffeeScript. (CoffeeScript allows “empty” interpolations.)$ coffee --version
CoffeeScript version 1.10.0
$ coffee -bpe '""" A test of #{"nesting" + func("""
quotes
""")}
inside each other
"""'
" A test of " + ("nesting" + func("quotes")) + " \ninside each other";
From @GeoffreyBooth on 2016-09-18 18:41
@greghuc To try to answer your questions:
From @greghuc on 2016-09-18 18:46
@lydell thanks for checking the bug - I can't reproduce it either on 1.10.0. I wasn't near a terminal, so was running Coffeescript with: http://js2coffee.thomaskalka.de/
From @lydell on 2016-09-18 18:51
@greghuc Use http://coffeescript.org/#try: next time ;)
From @GeoffreyBooth on 2016-09-19 19:38
@greghuc I invited you to become a collaborator on https://github.com/GeoffreyBooth/coffeescript. You’re welcome to use a new branch on that repo to implement this, if you want me or @lydell or @JimPanic to have write access to your branch. Or you’re more than welcome to work in your own fork.
When the branch is ready for a PR, please submit it against the soon-to-be-created 2
branch on https://github.com/jashkenas/coffeescript, unless you implement tagged template literals separately from template literals. If you submit the two separately, tagged template literals could go into master
(which is the 1.x, non-breaking-change branch) and template literals would go into 2
.
From @greghuc on 2016-09-20 18:18
@GeoffreyBooth I've spun out 2 new issues from the tagged template literals work:
From @greghuc on 2016-09-20 18:22
So I'll make a start on adding tagged template literals to Coffeescript next week (Sept 26 onwards), as per the proposal in this thread. My time for open-source commits is constrained, so I'm not providing an ETA for completion right now.
This will also be my first time changing Coffeescript, so there'll be a ramp-up period whilst I understand how it works.
Are there any pointers for getting an overview of the Coffeescript compiler?
From @GeoffreyBooth on 2016-09-20 20:34
Hi @greghuc, I put up https://github.com/GeoffreyBooth/coffeescript/wiki/How-parsing-works which was consolidated from a few helpful comments left for me while I was working on the modules PR. I plan to remove the specific references to modules and move it to the regular coffeescript repo wiki soon. There are other good pages there too: https://github.com/jashkenas/coffeescript/wiki
I also created https://github.com/GeoffreyBooth/coffeescript-gulp to automate compiling and testing.
From @jcollum on 2016-09-21 03:42
Hey I don't know where else to put this so:
I'm working on a new Hapi.js project the last few days. Decided to do it in ES2015 since a couple of junior devs are helping me. HOLY CRAP I MISS COFFEESCRIPT. All these damned extra braces and semicolons and such, clutterin' up my code. And the lack of easy key/value list comprehension. Etc.
So, thanks so much for participating here. Know that your work is appreciated.
From @GeoffreyBooth on 2016-09-21 04:00
Thanks @jcollum 😄 I think coffeescript6/discuss#32 is the issue you want 😉
From @GeoffreyBooth on 2016-12-05 01:57
Released in CoffeeScript 1.12.0.
From @greghuc on 2016-09-01 15:55
Current Coffeescript is incompatible with template literals, specifically tagged template literals. As such, direct support for template literals should be considered for coffeescript nextgen.
Specifically, calling a tagged template literal looks this:
Coffeescript already uses backticks to embed javascript:
Unless I've missed a trick, it's currently impossible to use a tagged template literal in Coffeescript by embedding the relevant ES6 javascript.
FYI, I ran into this problem when exploring the newish bel DOM templating library. Since it uses tagged template strings, I believe it's inoperable with current Coffeescript. This is the first time I've seen incompatibility between Coffeescript and Javascript..