Thinking about the use case for that, is there eta expansion for constructors? On the one hand, I’d think so. On the other, Foo in call(Foo) becomes ambiguous in whether it means passing the term Foo, or passing the eta expanded constructor of the class Foo.
class Creator(){}
def callit[A](f: () => A) = f()
callit(Creator _) //no: "Only function types can be followed by _ but the current expression has type"
callit(Creator) //no: "Not found: Creator"
callit(Creator(_)) //no: "Wrong number of parameters, expected: 0"
callit(() => Creator()) //yes
Should any of those no’s work? new makes it more obvious that you’re doing something other than just calling a method which makes it less surprising that the above no’s are no’s.
This topic has now been open for over 30 days. If anyone wants to add anything further or make any kind of closing or summary statement for the committee, please do so this week, before we close the topic.
I’m tentatively opposed, as I see the arguments of both sides as equal and negating one another, making the change a neutral net worth.
IMO, @jducoeur’s and @tarsa’s argument is the strongest argument in favor of the change – preventing developers from abusing case classes just because they provider new-less constructors.
On the other hand, I find @nafg’s and @morgen-peschke’s opposing arguments important as well – signal, not noise; blurring the distinction between regular and case classes; real work in constructors.
@Ichoran I am very much in the opinion that the new keyword has a purpose of reminding people of things they do need to be reminded about. Well, perhaps need is a strong word in this case, but I do think the keyword is not without significance.
new tells me that I am constructing a non-value class, that has non-trivial methods that my current unit is dependent on. In many cases, I would like to be able to mock this new class when testing my unit, and I can do this only if I inject said new class into my unit (via constructor or method argument).
If I may, I’d like to add another idea into the pile. How about an additional keyword / annotation that will make the compiler automatically generate an apply method?
@Apply
class MyLovelyFoo(bar: Bar)
// or
apply class MyLovelyFoo(bar: Bar)
// or
class apply MyLovelyFoo(bar: Bar)
This could help prevent the abuse of case classes for the sake of new-less constructors for those who do not care for the distinction between the types of classes or the significance of the keyword, while still allowing class authors who care about these things keep the distinction and the explicit constructors.
Perhaps with effect tracking of references we can request that a value can only be assigned with a new reference, which would also help in overload resolution of constructors vs apply:
class Foo()
object Foo:
// here fresh implies that only a fresh value can be returned,
// so this is not recursive, however fresh variables can not be assigned to this result
fresh def apply(): Foo = Foo()
def cache(fresh x: Foo): Unit = ???
cache(Foo()) // calls constructor unambiguously
Yes, but that is an illusion created on purpose (well, usually). Here the compiler itself forces this illusion. So without any apply methods, there shouldn’t be any difference for a given class Foo between new Foo and Foo, but there is.
There shouldn’t be any difference either way. How confusing!
If you want to create an anonymous class, you should mean it. Having to rely on subtle cues like “oh, no parameter block” or something isn’t a great way to do it. It should just be clearly very different, like (as I suggested)
This issue wouldn’t exist if some inconsistencies with classes and constructors were fixed.
For some reason, constructors always have at least one parameter list. If none is provided one is added implicitly, both at the definition and use site. If they behaved more like methods the compiler would say something like “missing argument list for constructor Foo”. You would have to write new Foo() { ... } instead.
Parameters to regular methods can be provided with {} instead of (). With a constructor suddenly you’re defining an anonymous subclass instead.
Anonymous inner classes typically couples both instantiation and declaration, which is problematic because each evaluation yields a new identity in the same svope as its declaration. Interestingly enough we already have a mechanism for that. How about allowing the following:
A toplevel object is a singleton, same as a toplevel val val a = new {...} would be a singleton. You can define an object in a class or def which is not a real singleton.
This is not the case for anonymous JS classes un Scala.js. an invocation like
val o = new Foo {
var x = 5
}
where Foo <: js.Any will not create an independent class. It will create an instance of Foo (or its parent class if Foo is a trait) and patch x directly on the instance.
This is a very important use case for interoperability purposes in Scala.js, which can be fairly common in code interoperating with JavaScript. That use case has to stay convenient, and must not be confused with declaring a local object (which does create an independent class).