Proposal: Real ES6 JS with Coffeescript syntax


It’s been a while since your original post. My team and I have been using CS as our main “language” in mid to large projects for more than 2 years. Everyone in the team is really happy with the way it’s written, how clean it is, etc.

I’ve been looking for a replacement to get closer to ES6 and your approach seems really clever. How is it going?



Badly. My goal was to be able to edit files from a JS repo as a CS-like file and commit the results back to the JS repo with no unnecessary diffs. Without this feature I don’t think it would be accepted by anyone. If you don’t agree let me know.

I put a lot of time into a utility that parsed JS -> AST -> my-new syntax and then back again. I got it working nicely. I didn’t have to test it with syntax differences (I’ve just implemented function <-> skinny arrow) because the hard part is the bi-direction translation.

However, the problem of preserving white-space exactly was surprisingly hard. Hard enough for me to shelve the project at the moment. The AST doesn’t keep the white space but I thought I could just annotate it. But that isn’t the problem. The problem is matching the code segments that go backwards. It didn’t know where to apply the white-space. You could have arbitrary sections of JS that are duplicates, e.g. for(i=0; i<n; i++). If code between them was edited you could lose their identity. At first I thought I had it working by keeping extensive metadata, but as I ran into bad cases the metadata would grow and grow. Then I ran into a case I couldn’t solve at all with my metadata.

I haven’t totally given up. I’ve spent idle brain cycles trying to think outside of the box. I’ve considered tracking the actual code segments in their entirety as the editing happened. This would limit the usage to using a single package to only edit inside the Atom editor but that is OK with me.

An opposite idea would be to calculate the resulting diffs over and over and rearrange the matching segments to minimize the number of them. The would work without even knowing what editing happened. This would be computationally expensive and probably a big-O problem. I’ve considered using a genetic algorithm. The good news it that it would only be slow upon saving with no computation while editing. But even one second of saving slowdown could be painful when waiting for an edit to render in a browser page.

Any ideas would be welcome.



It strikes me that if the project is using a code formatter like js-beautify, you wouldn’t have the problem. Convert back to JS, don’t worry about whitespace, run js-beautify, you’re done. The problem, of course, is that it requires an extra step for the JS programmers. (OTOH, they should be formatting consistently anyway). Point is, it may not be as big a problem for many people as you’d think.



The problem is that when I convert back to js it is beautified because I’m generating from an AST. I can’t think of any JS project that keeps code beautified. So mine doesn’t match and git diffs are splattered all over the code. It’s not like GO unfortunately.

If you want to start a project from scratch and enforce beauty then this could work for that case. It’s just not very practical considering the effort involved.



However, the problem of preserving white-space exactly was surprisingly hard.
An opposite idea would be to calculate the resulting diffs over and over

How about you compute diff once line against line, but use either a whitespace ignoring diff, or a diff over beautified line.

Once line are matched, I think whitepace could be matched by position.
If not, a character by character diff (alignment) could solve a resolve a lot.



This is what I was unable to do. I couldn’t identify the line that matched the original line.

Consider this …

// original js
x =1;
x= 1;

After editing and beautification I have …

// my pretty js
x = 1;

How do I know if it should be x =1; or x= 1;?

I know this case isn’t a problem but I’m just trying to show how I can lose the identity of the original line.



I know this case isn’t a problem but I’m just trying to show how I can lose the identity of the original line.

Now please consider the reason you are trying to match line. Aka to not pollute git diff.
The choice is ambiguous but at the end the diff will either show you have deleted line 2&3 or line 3&4, both equally clean. There is an ambiguity yes, but also no bad choice to make.

Maybe the problem fail to exist at place where diff fail to differentiate.



As you note, that example did not have a problem. The ones I ran into that caused me to shelve this were very hard problems. This was just to show how identities are lost. If I get some time I’ll resurrect the code and show an intractable problem.

I have examples that show this is not true.

BTW, I believed as you do that it was possible. That is why I spent weeks trying to get past problems. It is possible I was wrong but I doubt it.



I would like to see this working



Mark, what about using your work in the form of a CS like converter for projects that are willing to store their code in the pre transpiled whitespace significant form? For instance with tabs and other niceties and just generate the ES6 JS using a babel pre compile step of sorts? Is that not something you can use too? We would like to use something like this today since we are a core whitespace no C hangover shop :wink:



That would be a good first step. It would be important to have a ES6 to CES6 converter too, so people who write ES6 can collaborate. Then it is would be mostly a matter of automating the process to keep the files in sync, or even better, browser plugins to make the process transparent. Not sure if would be desirable to save both files in the project. Ideally the editing and conversion processes would be transparent, and they would just change the way the code is presented in the editor.



I’ve considered this and I’m not sure a coffeescript competitor makes sense. Coffeescript is improving, it has had generators for a year now.

At the moment I’m leaning towards editing options in Atom that allows one to edit in white-space significant code and convert to braces when saved. Just the one feature. I don’t remember for sure what thread the post was in, it was a thread I started about this project, but someone showed what javascript looked like with just the noise dimmed and it was pretty amazing.



You may be interested by Parinfer
Basically it try to infer lisp parenthesis from indentation.



I agree but jaskensas (creator of coffeescript) has indicated that it is unlikely coffeescript will adopt ES6 any time and I quote (“it will be something else just don’t call it coffeescript”) .I am a full time user who enjoys coffeescript a lot but want to move on with the official ES6 enhancements since browsers will get native speed improvements with them. If coffee syntax will not change, then a fork/clone with the other benefits of coffee is imperative. You seem quite a fair bit down this path and I fully agree that JS looks great (esp ES6 JS) with the C noise dimmed.

I have seen your proposal for the ATOM editor but its biggest problem will be custom formatting that all coders leave around when they are not editing in a whitespace significant environment. Heck, some of them want to work in the C brackets world because they don’t have to bother with indent.

SInce you clearly love the lack of noise, getting you over towards a coffeescript alternative would be awesome :slight_smile:



I love the idea, as long as it supports ES6 and is maintained beyond that. Which, if it simply allows plain JS to be typed without the eyesores, then it should, by default, always support the latest release.

Just this past week I began considering diving into CoffeeScript and as I stumbled upon this I was actually preparing to study. Now I’ve got the ol’ creative juices flowing again.

I have had a ‘toggle’ idea for a few weeks now, to toggle all the junk in the JS syntax, but haven’t attempted to write a package for it. I think JSW is a fantastic concept. though. Preserve the language in its entirety while giving it a makeover simultaneously.

Here is the ‘dimmed’ picture you were referring to recently. @mark_hahn

I’ve yet to dive into package development, so I may not have much to offer. Please do let me know if there is anything I, as a Vanilla JS developer of intermediate skill, could do to assist the project.

And also, thank you for all of your other fine work. I’m an avid user of your packages.


How to color brackets?

Dear all,

With all due respect, IMHO this is just a remedy. To make the poison a bit sweeter.
The sad truth is that coffeescript is dead and we all miss it.

So, there should be only one objective: to revive it!

CoffeeScript is dead, long live CoffeeScript !

I have just made a proposal for CoffeeScript version 6, aiming to compile towards ES6 (that would then be someone else’s job to execute or transpile.

Read it here and please vouch for it to happen! Also you can retweet this :slight_smile:

We can have CoffeeScript 6 by 2017!



The language couldn’t change much. Would this just be performance improvements? I have read that many of the new ES6 features run slower than the older equivalents. You pay for the extra level of abstraction. So how would this help coffeescript?



I proposed a similar thing, using Paren Free Mode with Pythonic newlines. I dropped significant indentation as too complicated when nested, evidenced by the mess CoffeeScript made of nested array and object literals. With just those few improvements to JS syntax, you get a much nicer language that is much more conventional than CoffeeScript.

number = 10

Parens around predicates are optional, but braces around blocks are required, even if it is a single expression.

if playerScore > topScore { topScore = playerScore }

Invocations are never implicit.

if playerOne.lives { restartLevel() } else { gameOver(playerOne) }

You can use the same syntax with loops too.

while player.isAlive { renderFrame() }

for invader in spaceInvaders { invader.attack() }

The lambda keyword is used for anonymous functions, which are paren-free, and require explicit returns.

square = lambda x {
    return x * x

Functions can have default args and splats and so on. Multiple args are separated by commas.

sum = lambda tally=0, args... {
    for arg in args { tally += arg }
    return tally

Arrow functions are exclusively used for expressing functions with a single expression in the body. Returns are implicit in these expressions.

square = x -> x * x

Removing implicit invocations removes complications, and there’s no reason why you couldn’t invoke functions that take a single arg using some kind of apply operator. This example uses a bar as the operator:

spaceInvaders.forEach | (invader) -> invader.attack()

There’s more to it, but you get the idea. You can significantly clean up JS syntax with a few choice improvements.