CARVIEW |
jashkenas / coffee-script
- Source
- Commits
- Network (6)
- Issues (7)
- Downloads (7)
- Graphs
-
Branch:
master
-
I do not think that writing a compiler in JS is the right way to go. rather, it would be nice if the compiler would be implemented in Coffee-Script itself.
This way you will have a more "relaxed" environment to maintain the compiler and at the same time will be as portable as the generated JS code.
On the down side the compilation stage would not be the most straight-forward considering the stackoverflow-based file I/O in JS ;)
Comments
-
bugxideax
Case one works, case two doesn't. Or am I missing something really obvious here?
Comments
Nice bug.
You're running into a JavaScript (mis)feature -- if you return an object from a constructor, that object will be the evaluated result of
new Klass()
, not the instance you were trying to make. Because, in CoffeeScript, all functions return their final value, you're getting the array back instead of aTest2
. Adding athis
as the final line of Test2's constructor fixes the problem.It would be ideal if CoffeeScript could detect constructor functions, and not add the return statement to their final lines, but unfortunately because any JavaScript function can be used as a constructor, I'm not sure that this is possible. It might be better just to document that
this
needs to be the final line of a CoffeeScript constructor. Any ideas?Well to be honest, the sort way returning works now is sort of magic and weird to me. I'm used to do an explicit return and if none was given it returns none or nil. So it seems to me that behavior would be a better fit in combination with javascript wouldn't it?
I'm not willing to give up implicit returns, just to fix this special case for constructors. They're critical for the whole "everything should be an expression" idea that underlies CoffeeScript.
I guess CoffeeScript could suppress the auto-return for functions that have names starting with a capital letter, CamelCasedClassStyle, assuming that they must be a constructor function, but that would also be quite strange if you aren't expecting it.
... a case for having a fully-fledged class model, e.g.
class Test1 { initializer: => this.test = "test" testFunction: => print("It works!") }
@Weepy: One of the reasons I dislike this idea is it is alot of reinvention. JS's prototype model means you can easilly roll mixins and other complicated things into the object system, whereas that sort of Class model makes it needlessly restrictive (i.e like Java)
As for the constructor thing, I don't like enforced naming styles. Can't we just add a "constructor" annotation?
blah = constructor a b c => this.a = a this.b = b this.c = c
I didn't mean that it should do anything like Base2, the code i suggested would compile like so :
var Test1 = function Test1() { this.initialize() } Test1.prototype = function() { this.test = "test" } Test1.prototype.testFunction = function() { print("It works!") }
You could take or leave the initialize idea (it's only an idea to make inheritance of the constructor more straight forward).
Another alternative would be to alias the prototype keyword (e.g like jQuery to fn)
Right, I am not suggesting changing semantics, but to fully convey the existing semantic richness of the prototype object system, you will need to come up with tons of new syntax, which I think is completely unneeded.
You're trying to cram Java like semantics into prototypal OO, and I don't like it.
I don't think I am. Really I'm just trying to figure out a different keyword to "prototype" which is both intimidating and confusing (particularly, to newcomers). It's also a pain to type :D
If we're going to address this issue, our answer needs to be capable of expressing the full semantic richness of JS's prototype system, as kamatsu says. The problem with exposing all of that plumbing directly is that it's very easy to break or mangle your prototype chain by accident.
I don't think that there's a place for Ruby or Java-esque classes in CoffeeScript. But we should have a convenient syntax for accomplishing the same patterns with prototypes, without the risk of messing up the chain.
extends
andsuper
are the start of it, but maybe a more holistic approach is needed, because of this constructor issue, and because it's pretty verbose to have to keep typingKlass.prototype.method:
, over and over, as you're defining a superclass (parent object).First off, CoffeeScript is lovely. Thanks for spending your time on it.
"You're running into a JavaScript (mis)feature..." Agreed, but a nice aspect of CoffeeScript is that it is nearly semantically identical to JavaScript. In this case, if a constructor is going to return a value, I also find JavaScript's behavior to be more intuitive than CoffeeScript.
Seems there are at least 3 solutions here:
1. Break semantic CS/JS semantic equivalence by having the constructor return a value. This is the current behavior. (I find this confusing.)
2. Kamatsu's suggestion is great, but it also breaks semantic equivalence by having the constructor never return a value. (Better...)
3. (2++) Adopt Kamatsu's suggestion, but allow functions to use the 'return' statements. 'return' would be implicit in all functions except for 'constructor', so the behavior would be standard CoffeeScript. However, a 'constructor' could 'return' which be equivalent to JavaScript. (Best?)See https://gist.github.com/267191 (a mod of https://gist.github.com/266191)
Hey Alson. Thanks for the patch. I have a couple of concerns:
- Any function can be used as a constructor by writing
new func()
. So, having to manually tag constructors yourself isn't very foolproof. - Even if it was, it's not any easier to tag a function with
constructor
than it is to just put athis
at the bottom of the function, and the latter doesn't have to introduce a new keyword.
So I'm thinking more along the lines of a complete complete class-definition syntax, if we pursue this. You'd have to have a clean way of saying: here is a class, this is its name, these are its methods, these are its properties, this is the constructor.
A lot of the current verbosity in creating a class lies in repeatedly writing
Klass.prototype.method: ...
when defining methods. What do y'all think of having a real prototype assignment operator? This:Bird.fly:: wind => airspeed * wind * effort
Could compile into:
Bird.prototype.fly = function fly(wind) { return airspeed * wind * effort; };
I'd don't like how it looks like it's a method on Bird itself, prefer sthing like
Bird::fly wind => airspeed * wind * effort
Yeah, you're right, and it doesn't even save that much. Nevermind.
Taking the example from the current documentation, what do y'all think of something like this:
class Animal constructor: name => this.name: name move: meters => alert(this.name + " moved " + meters + "m.") class Snake extends Animal speed: 5 move: => alert("Slithering...") super(this.speed) class Horse extends Animal speed: 45 move: => alert("Galloping...") super(this.speed)
All methods and properties would be defined on the prototype, and the constructor would return "this", naturally.
How would you go about adding further methods to the prototype. Perhaps
Bird::fly
isn't so bad after all.Also, in this example, how would the base constructor get called from the subclasses?
If you wanted to add methods to the prototype from outside, you can just add them:
Horse.prototype.canter: => ...
You should be able to call your ancestor's constructor using
super()
, like any other method.Sorry - just to be clear, in this instance
class Snake extends Animal
, the Animal constructor would not be called?I.e. as with normal JS, the constructor is not subclassed (something that's unfortunately confusing for newcomers).
Hmm, in the case above, subclasses like Snake should call Animal's constructor unless they choose to override it, no? It might be tricky to implement.
That's what it should do, but as you say implementation might be tricky. Coffee could keep track of implemented classes, but seems out of the question.
I suggested an implementation where each constructor automatically called an 'initialize' function with the inbound params which would then be subclassed automatically. The only problem I can see with this is that it means that every class would have an initialize function.
Jeremy,
w.r.t. creating a real Class mechanism: one of the aspects of CoffeeScript I really like is that it's basically just pretty JavaScript. Creating a Class mechanism would add to CoffeeScript something that is not within JavaScript, so I'd be less excited about that.
If you delete the word "Class" from your sample code, wouldn't you have clean CoffeeScript that is concise and doesn't imply anything special?
Animal: constructor: name => this.name: name move: meters => alert(this.name + " moved " + meters + "m.") Snake extends Animal: speed: 5 move: => alert("Slithering...") super(this.speed)
subclasses like Snake should call Animal's constructor unless they choose to override it, no?
From my perspective, Javascript doesn't do that, so I wouldn't expect CoffeeScript to do so.
Alson
Yeah, I agree completely, which is why there's been so much discussion on this page, and why we haven't moved forward with any of the proposals yet -- I don't think that my most recent posted one is very satisfactory.
Ideally, we can come up with something that keeps JavaScript's prototype flavor (avoiding
class
), encapsulates the common patterns for setting prototype properties and prototype chains, and provides a greatly shortened syntax. I don't think we've seen that yet -- the field is still wide open.One last note: object prototypes already have a constructor member, so 'constructor' is probably a bad term to use (lest people be confused about whether they're modifying widget.prototype.constructor or not). Maybe '_'.
Animal: _ : name => this.name: name move: meters => alert(this.name + " moved " + meters + "m.") Snake extends Animal: _ : name => super(name) speed: 5 move: => alert("Slithering...") super(this.speed)
This syntax is pretty nice and is just sugar. 'Snake extends Animal' is a little complicated because the constructor needs to be pushed up to run before the object is extended...
The way I tend to write objects in JS is perhaps appropriate here:
var animal = function() { //mixins added here mixin_stuff(this) var private_function = function (a,b) { } var private_variable; //constructor goes here this.public_function = function () { //Note how private stuff is inherited via closure } this.public_variable = 5; }
I believe inheritance is a broken model (I am a haskell programmer by trade) so I tend to use mixins exclusively.
Anyway, the only problem faced by my object model in coffee is that I have to add "this" at the end. The model you are proposing is different, and unfortunately doesn't allow me to make private data via closure. That's why I'd rather a smaller change that just prevented the unsightly "this" from appearing at the end.
I was just playing with the example at the start - it's quite odd, since it works if the return value is a string, but not an array or and object. Quite odd?!
We could do somethign like:
Use::
or similar for an alias tothis.
, unless it's preceded by a variable name, in which case, it's an alias toprototype.
. So without the Class stuff, an example would be :Animal: name --> ::name = name ::move = meters --> alert(::name + " moved " + meters + "m.") Animal::hello = --> alert("hello")
(I was just also experimenting with using = and --> in above)
I'm getting a bit confused in this thread... It started off simple (constructor behavior is unexpected) and got pretty complicated (I think there are now a number of separate features within this thread). Maybe we could move this discussion off onto a wiki page and do the following:
Add some real world, fairly large sample code gists. The gists we're looking at here are so short that the benefits are both minimal and different for each gist. Enumerate the features: 1. Align constructor behavior with JS's (maybe) 2. Provide a bit of sugar for long .prototype. sections 3. Provide a bit of sugar for long this.* sections 4. Provide a bit of sugar for Classes (?) 5. ???Assuming that y'all are okay with that, I've added a wiki page to my repo and made you all collaborators. Feel free to edit to taste, to add example code, etc. See here:
https://wiki.github.com/alsonkemp/coffee-script/cs-issue-17And shouts out to any Haskellers who're reading...
sorry alson - here's another idea to add to the melting pot: supress the return value if the function starts with a capital (it would still be possible to explicitly return from there functions).
Weepy,
Added. You're welcome to update the wiki page, too.
Alson
Sorry to be continuing the discussion here, but wikis really don't lend themselves for discussion, imho.
One problem that always crops up in JavaScript is that if the binding of a function changes (as is common for click events for example), it really screws up any attempt at using the 'this' keyword in a constructor. A common workaround is to assign 'this' to some other variable, such as 'self'.
I really like wimpy's suggested syntax, maybe CoffeeScript could do some analysis, like it does for scoping of variables and automatically resolve the "instance" variables correctly:
Animal: name => @name: name @move: meters => click(event => alert(@name))
would compile to:
var Animal = function(name) { var _this_12345 = this; // added because we are defining stuff with @ _this_12345.name = name; _this_12345.meters = function() { click(function(event) { alert(_this_12345.name); }); }; };
I have no idea how hard this is to implement, but it would remove a lot of the pain associated with working with constructors.
While I would like capitalized names, I sort of hesitate to use it as it reduces the ability to make partial ports of existing JS applications to coffee.
@alsonkemp: Haskell hi-5!
Yes - I feel the same about the capitalized. I think we just need a 'class' operator or similar that suppresses the return (note that you don't have to return this, you just have to avoid returning an array or an object) and handles any 'extends'.
Wasn't really thinking about that part of it. I agree, capitalization to determine if a function is a constructor is bad, especially since we can do stuff like:
Animal: x => this.x: x. animal: Animal
Now
animal
is a constructor, even though it is lowercase? - Any function can be used as a constructor by writing
-
#1) Started a JavaScript version of the compiler using OMeta JS 2.0:
https://www.tinlizzie.org/ometa-js/#CoffeeCurrently sub-classing the Javascript Compiler, so it can execute CoffeeScript aliases or JavaScript, but may need to redo it as a complete compiler in order to do CoffeeScript Statements.
Not sure how much time I'll have to work on this, so anyone else is welcome to to take it over.#2) Suggest that Objects should be either CoffeeScript format OR standard JSON format to make it easier to read JSON directly without parsing to CoffeeScript or visa versa.
With the newlines it looks good, but I think
ages: {max: 10, ida: 9, tim: 11 }
is slightly more legible than
ages: { max: 10 ida: 9 tim: 11 }#3) In your Docs [ https://jashkenas.github.com/coffee-script/#array_comprehensions ]
Suggest you change the example from:# Eat lunch. lunch: food.eat() for food in ['toast', 'cheese', 'wine'].
to something like:
# Eat lunch. lunch: eat(food) for food in ['toast', 'cheese', 'wine'].
eat: edible => log("yum: " + edible).
log: msg => if (window && window.console) console.log(msg) else alert(msg).This works better than having to define eat in String.prototype.eat
Otherwise you end up eating Strings instead of Food! ;-)
Comments
- Awesome! It's not often you can ask for something on a wishlist and have it come true.
Leaving out the commas in array and object literals is only allowed in the newline form. I quite agree with you -- it doesn't make any sense in single-line objects. Note that you're allowed to mix and match:
matrix: [ 0, 1, 1 1, 1, 0 1, 0, 1 ]
I've changed the example -- it'll go out with the next release. Thanks for the doc fix.
-
bugx
This:
a unless b for i in [0..5]
gives:
/dev/stdin: line : parentheses can't be wrapped around a statement
Comments
I can add line numbers to the error, but that's not the real problem. Actually, your case can easily be fixed by allowing
unless
to compile as a ternary (it forces a statement at the moment). But this raises a much more important issue: things that compile into JS statements can't really be used as expressions.This is the biggest missing piece of CoffeeScript. We need a way of generally transforming all of the nodes that compile into statements into expressions. Right now, you can return any statement or assign its result, pushing those operators down inside the statement itself, which knows how to handle them. But it ends there. You can't wrap parentheses around a statement, or pass it as an argument to an function, or chain method calls off of it.
I'm not sure if its possible to fix any of those problems, even in theory, but it's worth thinking about. How could you transform the AST to make this compile correctly?
result: (x * x for x in [1..10]).reverse().join(' ')
That looks like it should be compilable, but to make it happen, we'd have to inject the assignment, the parentheses, and the chained methods that come after (later on in the AST), all into the ForNode somehow.
Easy, compile it into a closure, so that that would compile to
result = function () { //function that returns the whole list comprehension thing }().reverse().join(' ')
-
I just started playing with CoffeScript, really great work guys, it's a work of art! There probably already is a nice idiomatic solution for this and I jsut don't know it, I just thought I'd ask (a mailing list would be awesome!):
When passing functions as arguments, it looks really good with single line functions:
items.map(item => item.value().)
But when doing the same with muliple line functions, it looks not so great:
items.each(item => item.focus() item.remove. )
Those extra parens really noise up the code in a bad way. This is where Ruby's block syntax really shines, maybe there could be something similar added, such as:
items.each -> item item.focus() item.remove.
Or with arguments:
items.inject(4) -> sum, item sum + item.value().
Which would be quivalent to:
items.inject(4, (sum, item => sum + item.value().))
What do you think?
Comments
I think it might be better if there was a consistency between the position of the arrow and single-line/multi-line versions. eg:
Also, I proposed this in the "postfix block expression" issue, and it was canned.
I just read that issue, I disagree that this is too limited. Most functions in well written JavaScript only take a single function as an argument, and it'd usually be the last argument (notable counterexamples are setTimeout and friends). So this is really adding sugar for a very common pattern.
-
Ruby style interpolation would be awesome. E.g.
a: "dog" b: ['f','o','x'] p = "The #{b.join} jumps over the #{a}"
compiles to:
var a, b, p; a = "dog"; b = ['f','o','x'] p = "The "+(b.join)+" jumps over the "+(a);
or
var a, b, p; a = "dog"; b = ['f','o','x'] p = ["The ",b.join," jumps over the ",a].join("");
Should be fairly easy to implement (guessing here)
Comments
-
in coffescript isn't possible make multiline methods chainable alla jquery:
example
$(".team") .find("player") .css("color","#000000")
give this error:
syntax error for '.', unexpected property_access
Comments
- bug▾
- enhancement▾
- fixed▾
- idea▾
- wontfix▾
- Apply to Selection
-
Change Color…
Preview:preview
- Rename…
- Delete




I think that would be pretty awesome, self-hosting the compiler would be like eating-your-own-dogfood and the language would be improved when issues arise in making an application with it
I'm not a big fan of dog food, I prefer to prepare tasty human meals, and this is one of the techniques that helps differentiate the two... plus it is a great test suite for a language ;)
It would be wonderful to try it. The lexer and code generation should be fairly translatable to CoffeeScript -- the trick is the parser. It would either have to use a JavaScript parser generator (I'm not sure if there are any yacc-equivalent ones in JavaScript), or we'd have to roll our own. I'm not going to be tackling this one any time soon, but if anyone wants to give it a shot they're more than welcome.
For me this is a chicken-vs-egg issue, I'd love to make the language self-hosted, but I do not want to install yet another interpreter (I mean Ruby) thus I'm waiting for it to get self-hosted.... and I do not want to waste time and write yet another bootstrap version of the interpreter/compiler in some other language just to throw it away when I'm done.
I'll think about this some more :)
However this will play out, I'd be very interested in helping. I think 2010 will be the year of the javascript superset languages.
JS is a really good language if you know how to use it, and if ECMA finally fixes several quirks that exist in the standard -- without creating an bloated elephant, which is the current draft .
IMHO, the only thing that can challenge JS at what it does, is something both more productive and efficient at the same time. and that is a real chore, because the simpler the work is the more one can do and the more one can do the more stress you apply to the VM, so efficiency, at some point will complicate things substantially.
I might not share your perspective on the L2 languages on top of JS, but this is something really fun and IMO quite worth a try! ;)
So is C, but I get a lot more done with Objective-C. And I can still just drop to C if I need to, same as I would like to do here. And although I trust these experts to come up with a great draft, I'd love many others to implement their own languages that I might like better and would work right away in every browser.
Well, ObjC is one great language and a great architecture... something I can't say of another "C based" language, the C++...
I'm not saying that it should not be done, rather that it should be tackled with care :)