-
Notifications
You must be signed in to change notification settings - Fork 2k
Fix default constructor for subclasses of native objects #2359
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I totally agree, there is absolutely zero need in Sacrificing these great features to make external constructors work better. +10 |
@michaelficarra -- how do you feel about this? I'll just briefly reiterate my general antipathy towards returning "other typed" objects from constructors... |
@jashkenas: It seems like a bug. The default constructor of a sub-class shouldn't return the result of the |
@michaelficarra Doesn't it contradict #2111? |
Sure, yeah. But I don't think we actually want the behaviour that was being requested there. We just happened to already have that behaviour. And hopefully we will correct it here. I think we just need to add a edit: After thinking about it again for a minute, it's probably not as simple as I originally thought. It's something like |
@michaelficarra Also, this. If the default constructor of a subclass doesn't return the result of |
@jashkenas: Changing this behaviour may cause some users' code to break in a pretty sneaky way. I definitely think it's worth fixing, though. Minor version bump when this lands? |
Ok ... but why would you want to not |
It's awesome to see the general support of this idea! =D I'll try to get my hands on this in the weekend if i have time, but the code on @michaelficarra i've just noticed that you have a Kickstarter project to make a better CoffeeScript compiler and it was successfully funded just yesterday. Congratulations man! And good luck with that! |
Well, i've been playing with this a bit. The problem indeed comes from the behavior of If the test "#2359: instanceof should work when extending native objects", ->
class MyError extends Error
ok new MyError instanceof MyError But it adds a quite nasty inconsistency: "other typed" (i like this term =P) external constructors don't work as expected in subclasses, nor do inherited "other typed" constructors. This (new) tests fail: test "'other typed' external constructors should still work in subclasses", ->
ctor = -> {}
class A then constructor: ctor
class B extends A
ok (new B) not instanceof A
ok (new B) not instanceof B
test "'other typed' constructors should be inherited", ->
class A then constructor: -> return {}
class B extends A
ok (new B) not instanceof A
ok (new B) not instanceof B (note that both these tests pass on the current Both of these inconsistencies, as i see it, arise from trying to deal with these "other typed" constructors, which i also think is a questionable behavior. I personally don't see how we could fix this issue (and i'm using the word "fix" just because it has been marked as a bug) and maintain the behavior of "other typed" constructors consistent. If the test "#1966: external constructors should produce their return value", ->
ctor = -> {}
class A then constructor: ctor
ok (new A) not instanceof A Which is the one that deals with "other typed" external constructors. I personally would prefer to get rid of "other typed" constructors altogether: even forbid returning anything other than |
I concur. Let's remove 'em. |
Wait, this would remove the "return" statement from the default constructor? That would be problematic for the QuickUI framework, which now depends on that feature for concise UI component creation. (See http://quickui.org/docs/CoffeeScript.html for an explanation of how you easily create UI component subclasses with CoffeeScript's "class" syntax.) It turns out that there's at least one very commonly-used JavaScript class that makes heavy use of an "other typed" constructor: jQuery. As described in http://blog.quickui.org/2012/06/07/jquery-fn-init/, jQuery's constructor supports both a static form (without "new") and a normal form (with "new"). QuickUI components are, by design, subclasses of the jQuery class. This means that any UI component created in QuickUI can be directly manipulated using jQuery methods such as Since coming across CoffeeScript last year, it's been a goal of mine to let UI developers create new QuickUI component classes as concisely as possible in CoffeeScript. QuickUI's earliest support for CoffeeScript required that each component class include a boilerplate constructor that did the "return" required here. Experience proved that even requiring a single line of boilerplate in a class was something of a pain: it was all too easy to forget the magic line, and the consequence of omitting that line was unfortunately not something that made it obvious where the bug was. Furthermore, that line stuck out like a sore thumb in otherwise a highly streamlined CoffeeScript class definition. With CoffeeScript 1.3.1, the boilerplate constructor could be dropped, and UI component code in CoffeeScript looked much nicer. (For a more interesting sample of web app UI written in QuickUI and CoffeeScript, see http://quickui.org/docs/contacts.html.) Subclassing jQuery represents an extreme edge case but, to QuickUI at least, it's a highly useful thing to do. From the perspective of a QuickUI developer, it would be a significant loss to drop the implicit "return" from the default constructor generated by CoffeeScript. |
@JanMiksovsky Thanks for the interesting reply. After some time trying to understand what It's really unfortunate that that same behaviour is what's causing this issue when extending native objects :( |
Fix should be merged now. |
Maybe this should be labelled as "wontfix" instead of "fixed" now that #2599 is reverted in case anyone wanders into this problem in the future. |
@epidemian Is there no way to fix this and keep the other-typed constructor behavior? |
@xixixao, technically, yes, it should be possible to fix this particular issue while keeping some compatibility with other-typed constructors. (Disclaimer: this issue is very old; i had to dig through various issues and comments to re-understand some of it, so i might very well be missing many things here...) The first commit for #2359 (e46b129) only fixed this issue actually; without removing the ability to return other objects from constructors. Having only that fix, however, would mess up other-typed constructors in another way: they wouldn't be inheritable. # CoffeeScript < 1.5.0 or >=1.6.0
class Base
constructor: -> return {}
class Derived extends Base
(new Derived) not instanceof Derived # => true
(new Derived) not instanceof Base # => true
(new Base) not instanceof Base # => true So that would introduce a new inconsistency to the language. I think that, if other typed constructors (or, actually, constructors that return something other than †: An unfortunate consequence at that. But just like |
Just to make sure: Why can't class A extends Error
constructor: -> super be the implementation when no constructor is declared? |
Because doing that would, for example, break code like QuickUI (which @JanMiksovsky mentioned a couple of messages back in this thread), or any other code that relies on constructors that return other objects being inherited. Would you prefer to allow having other typed constructors (or external constructors) in superclasses but, at the same time, not have them inherited in subclasses? That would cause this inconsistency: class Something
constructor: ->
return new SomethingElse()
class SubSomething extends Something
# implied constructor: -> super
(new Something) instanceof Something # => false
(new Something) instanceof SomethingElse # => true (as "expected", because Something has an other typed constructor)
(new SubSomething) instanceof SubSomething # => true
(new SubSomething) instanceof SomethingElse # => false (it didn't inherit the constructor of Something, even if we didn't override it!) Now, don't get me wrong: i'm not advocating for the use of other-typed constructors here. I think that "feature" of |
I was trying to subclass
Error
to handle different kind of errors, and got some pretty weird results:False? WTF?
Now, after digging a bit and asking about this in Stack Overflow i could figure out why is it that i need to write...
... in order for
new MyError instanceof MyError
to work properly. You can read the SO question if you don't know why, but, basically, the generated JS constructor changed, in CoffeeScript 1.3, from this:to this:
Which is equivalent to calling
Error.apply(this, arguments)
which returns a newError
object instead of thethis
that was passed to it, and that is what finally gets returned when you donew MyError
.Now, i can understand the motivation for introducing this behavior in CoffeeScript 1.3.1 (it seems that
new A not instanceof A
is even a desired behavior sometimes), but i think this behavior has more problems than benefits.First, it makes subclassing native objects inconsistent, as with "normal" CoffeeScript classes you don't need to define a constructor in the subclasses for them to work:
Also, as noted in issue #1966, it also breaks subclasses of native objects completely if a constructor is not defined in the subclass:
Now, the need for subclassing native objects like
Array
is something that can be criticized, yes. But i think that subclassingError
is not so far fetched; shouldn'tclass MyError extends Error
Just Work™? :DWas the behavior of the generated JS constructor changed just to support returning an explicit object (different from
this
) from a constructor? If that is the case, why is that supported at all? I mean, why would someone want to return a different object from a CoffeeScript constructor? You can always write a JS-style function for that...But i don't see the point in allowing this (at least not when taking the problems mentioned above in consideration) inside CoffeeScript little nice syntactic sugar for classes, a syntactic sugar that is meant to make writing classes and inheritance easier.
So i open this issue mainly to request 080ed2e to be reverted, but i'm also really interested in knowing other people's opinion on this matter :D
The text was updated successfully, but these errors were encountered: