Skip to content

new-less object construction does not work on secondary constuctors #7217

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

Closed
aappddeevv opened this issue Sep 13, 2019 · 9 comments · Fixed by #7224
Closed

new-less object construction does not work on secondary constuctors #7217

aappddeevv opened this issue Sep 13, 2019 · 9 comments · Fixed by #7224

Comments

@aappddeevv
Copy link

I was calling the new-less object secondary constructor but it would not work without using new.

Boiled down example from my code:

class Foo(p1: String, p2: String):
  def this(p1: String) = this(p1, "blah")

val x = Foo("blah", "hah")
val y = Foo("blah")

I needed to add new Foo("blah") to get y to compile.

@aappddeevv
Copy link
Author

Hmm..this works in the repl...but my program is not working when I do this. It may be due to other factors. I'll close until I can sort this out.

@aappddeevv
Copy link
Author

aappddeevv commented Sep 13, 2019

Well, I just ran into this again in another code area. I'll try to identify the conditions under which "new" is required when creating an instance. It appears to be happening inside single statement block.

@liufengyun
Copy link
Contributor

@aappddeevv Thanks for reporting.

I'm closing this now as the given code snippet compiles without problem. When you get a snippet that does not compile, please feel free to update the issue and re-open.

liufengyun added a commit to dotty-staging/dotty that referenced this issue Sep 13, 2019
OlivierBlanvillain added a commit that referenced this issue Sep 14, 2019
@aappddeevv
Copy link
Author

Wait, does this mean that there was an issue? I just did this on 0.18

cala> case class Foo(msg1: String, msg2: String) {                                                                     
     | def this(msg1: String) = this(msg1, "blah")
     | }
// defined case class Foo

scala> Foo("blah")
1 |Foo("blah")
  |^^^^^^^^^^^
  |missing argument for parameter msg2 of method apply: (msg1: String, msg2: String): Foo

scala> Foo("blah", "hah")                                                                                         
val res4: Foo = Foo(blah,hah)

scala> new Foo("blah")                                                                                        
val res5: Foo = Foo(blah,blah)

@liufengyun
Copy link
Contributor

liufengyun commented Sep 16, 2019

Thanks @aappddeevv .

It seems to be overloading resolution problem related to Foo.apply -- without case, everything works perfectly:

case class Foo(msg1: String, msg2: String) {
  def this(msg1: String) = this(msg1, "blah")
}

val foo: Foo = Foo("blah")  // error if we have `case` for `class Foo`

@liufengyun liufengyun reopened this Sep 16, 2019
@liufengyun
Copy link
Contributor

liufengyun commented Sep 23, 2019

As discussed in an internal meeting, the proper way to add another constructor for case classes is to introduce an apply in the companion object:

case class Foo(msg1: String, msg2: String)

object Foo {
  def apply(msg1: String) = new Foo(msg1, "blah")
}

val foo1: Foo = Foo("blah")
val foo2: Foo = Foo("blah", "blah")

The rule is that, if a companion object contains apply, it's always preferred over any class constructors in new-less object construction. The 2nd constructor can be called with an explicit new.

@aappddeevv
Copy link
Author

I think as long as the docs reflect this, I don't believe they did, then it is the intended behavior.

@liufengyun
Copy link
Contributor

@aappddeevv Thanks for pointing out, the specification [1] says the following:

Given a function call f(args),

  1. if f is a method applicable to args, typecheck f(args) unchanged,
  2. otherwise, if f has an apply method applicable to args as a member, continue with f.apply(args),
  3. otherwise, if f is of the form p.m and there is an implicit conversion c applicable to p so that c(p).m is applicable to args, continue with c(p).m(args)

The current behavior is consistent with the 2nd rule.

[1] http://dotty.epfl.ch/docs/reference/other-new-features/creator-applications.html

@aappddeevv
Copy link
Author

That works!

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

Successfully merging a pull request may close this issue.

2 participants