Skip to content
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

Optional "new" to instantiate classes? #861

Closed
TomasM opened this issue Nov 22, 2010 · 13 comments
Closed

Optional "new" to instantiate classes? #861

TomasM opened this issue Nov 22, 2010 · 13 comments

Comments

@TomasM
Copy link

TomasM commented Nov 22, 2010

Is this a good idea? Currently if you instantiate a class without "new" it just returns undefined.

Or should this be left for the developer, since now we have executable class bodies?

@StanAngeloff
Copy link
Contributor

It would be possible to embed this behaviour in generated classes. The size of the generated code will be affected however, but it is something I do quite a lot come to think of it. I've also been seeing it more and more in node. Here's my way of handling new:

class Process
  constructor: (@command, @resume) ->
    return new Process arguments... if this not instanceof Process

+1

@satyr
Copy link
Collaborator

satyr commented Nov 22, 2010

Check out Coco, where you can define bound constructors for this particular effect.

You can compare https://github.com/satyr/coco/blob/master/src/ast.co and https://github.com/jashkenas/coffee-script/blob/master/src/nodes.coffee to see how it enjoys new-free classes.

@TomasM
Copy link
Author

TomasM commented Nov 22, 2010

It looks like since 0.9.5 classes are very similar to coco's. The new-free thingy would be a great addition, in my opinion. When I code js I always do this anyway.

@hen-x
Copy link

hen-x commented Nov 23, 2010

While Process() and new Process() would become equivalent, Date() and new Date() would not. If Process() is the idiom inside of Coffeescript, then new becomes a legacy keyword for calling classes defined outside of Coffeescript, whether built into the environment, or imported from a library. The question is, do we want to have to think about where a class was defined when deciding how to call it? When we intentially include the new to support a legacy constructor, will the decision be self-documenting, or would it require an explanatory comment? Is the cognitive overhead of the distinction between Something() and new Something() a reasonable trade-off for the convenience of dropping a keyword?

@satyr
Copy link
Collaborator

satyr commented Nov 23, 2010

a legacy constructor

Why "legacy"? Behaving differently for [[Call]] and [[Construct]] is perfectly reasonable. The culprit is that ES3/5 doesn't expose right APIs that handle this capability.

cognitive overhead of the distinction between Something() and new Something()

Newless constructors remove this overhead. Remember that current CoffeeScript classes do behave differently without new, most likely doing harmful things.

@hen-x
Copy link

hen-x commented Nov 23, 2010

"Legacy" in the sense of preexisting, or defined outside of Coffeescript. Poor wording on my part.

To clarify my concern: in JS, calling a constructor without new is usually an error, producing unexpected results. Allowinng CS-defined classes to be be constructed without new makes the language more forgiving of this mistake, turning it into a feature. It removes the need to think about whether or not new is needed when invoking a constructor. Unfortunately it doesn't remove the need to use new for classes defined outside of CS, and I suspect it will cause more errors and more unexpected behavior.

@StanAngeloff
Copy link
Contributor

You can do Number(x), Boolean(value), Array(10), RegExp('...', ''), Error('...') so that pretty much leaves Date() which still gets you a string.

Now that I think about it, new and () are a nice way to get singleton instances and that requires you to have control over which one gets executed.

# one way of doing it; ofc you can return an object bag,
# implement it using safe-guards even when called with new,
# etc. but that's not the point.
class Process
  instance = null
  constructor: ->
    return instance or= new Process arguments... if this not instanceof Process
    console.log 'Initialized'  # called once

a = Process()
b = Process()
a.prop = 2
console.log b.prop  # same as a.prop

So either make it explicit or use a new language construct... or leave it as is and let devs sort it out.

@satyr
Copy link
Collaborator

satyr commented Nov 23, 2010

We don't write new jQuery because we know that's redundant.

I suspect it will cause more errors and more unexpected behavior

Concerning about changing users' habitude is pointless for Coffee, which has done plenty of harm in that regard already (e.g. inof, =====).

@hen-x
Copy link

hen-x commented Nov 23, 2010

Concerning about changing users' habitude is pointless for Coffee, which has done plenty of harm in that regard already (e.g. in→of, ===→==).

Sure, but this isn't about consistency between CS and JS. It's about consistency within CS itself. This proposal entails taking a piece of syntax that already causes confusion -- new -- and making it optional some of the time. The user of the code assumes the responsibility for remembering when it is optional and when it is mandatory.

@satyr
Copy link
Collaborator

satyr commented Nov 24, 2010

the responsibility for remembering when it is optional and when it is mandatory

CS can never get rid of this responsility as long as it compiles into JS. Making new optional itself is a JS thing and doesn't require CS at all. It improves security and {read,writ}ability wrt that class, but doesn't propagate further.

@jashkenas
Copy link
Owner

I think that sethaurus' arguments here are really sound. I agree entirely. new is with us, for better or for worse. Closing the ticket.

@epidemian
Copy link
Contributor

Given the pull request that would invalidate new-less constructors in CS-style classes (#2599), i feel compelled to do some necromancy and comment on this issue.

new is quite an edgy feature of JS, and i would love to see it gone from CS. Unfortunately, as @sethaurus noted, it would be impossible to seamlessly inter-operate with other JS code if we were to get rid of new altogether.


That being said, i don't like saying "it's impossible" without providing alternative solution(s). JS-style classes can still be used for this kind of things. Stealing @davidchambers' example, with JS-style classes:

Point = (x, y) ->
  return new Point x, y unless this instanceof Point
  @x = x
  @y = y

# Let's add a method to make it more interesting.
Point::add = (point) ->
  Point @x + point.x, @y + point.y

p = (Point 4, 5).add Point 6, 7

Or, if you really abhor JS-style classes and really really want to have new-less constructors in CS-style classes, i guess you can always resort to some black magic:

# Auxiliary function to make classes that can be instantiated without `new`.
newLess = (ctor) ->
  F = (args...) ->
    return new F args... unless @ instanceof F
    super
  F extends ctor
  F

Point = newLess class
  constructor: (@x, @y) ->

  add: (point) ->
    Point @x + point.x, @y + point.y

p = (Point 4, 5).add Point 6, 7

Yes, quite a bit of magic. But, on the other hand, the newLess function is defined only once and can be used for many classes, therefore eliminating the boilerplate duplicated logic in all those constructors. There probably is a better way to do this though 😅

@vendethiel
Copy link
Collaborator

class WithBoundConstructor
  constructor: =>
    alert @ instanceof constructor

WithBoundConstructor() # alerts true

turned down several times :p.

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants