Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Case class toString method shows field names with values #6936

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
wants to merge 4 commits into from

Conversation

joshlemer
Copy link
Contributor

@joshlemer joshlemer commented Jul 15, 2018

Currently, case classes have their toString method generated by the compiler, to print like this:

case class A()
A().toString // "A()"

case class B(i: Int)
B(1).toString // "B(1)"

case class C(i: Int, j: Int, k: Int)
C(1,2,3).toString // "C(1,2,3)"

This Pull Request adds the field names for more clear reading, so the above will now look like:

case class A()
A().toString // "A()"

case class B(i: Int)
B(1).toString // "B(i=1)"

case class C(i: Int, j: Int, k: Int)
C(1,2,3).toString // "C(i=1, j=2, k=3)"

For inspiration, see Kotlin's data classes:

data class A(val a: Int, val b: String)

fun main(args : Array<String>) {
    println(A(1, "hello")) // A(a=1, b=hello)
}

I have not yet added integration tests to this PR, as I'm not really sure how to set these up just yet.

@scala-jenkins scala-jenkins added this to the 2.13.0-M5 milestone Jul 15, 2018
@NthPortal
Copy link
Contributor

THANK YOU SO MUCH

@NthPortal
Copy link
Contributor

re: testing, what you might do is add a partest case to run (i.e. test/files/run/<test-name>.scala and test/files/run/<test-name>.check). The test can define a case class like one of the examples you gave in the PR description, and then print an instance of it. e.g.

case-class-toString.scala`:

object Test extends App {
  case class A(i: Int, s: String)
  println(A(1, foo))
}

case-class-toString.check:

A(i=1, s=hello)

@sjrd
Copy link
Member

sjrd commented Jul 15, 2018

Sorry, but I have to voice a negative vote on this one. This is going to break so much code, if only existing tests, that the convenience is never going to be worth it.

If only we had done that while we could, like in 2.9.x or something. But now is too late.

@NthPortal
Copy link
Contributor

it might be worth opening a thread on contributors before doing more work, to see what the consensus of others is (if there is one)

@joshlemer
Copy link
Contributor Author

Mentioned this at https://contributors.scala-lang.org/t/case-class-tostring-new-behavior-proposal-with-implementation/2056

@metasim
Copy link

metasim commented Jul 15, 2018

@sjrd Is the argument against ("is going to break so much code") from the standpoint that people out there parse the string output of case classes? Or is there some other consideration?

@retronym
Copy link
Member

Is the argument against ("is going to break so much code") from the standpoint that people out there parse the string output of case classes? Or is there some other consideration?

Unit tests often make assertions like assertEquals("List(CaseClass(32))", list.toString. It is a bit lazy, but sometimes its the most concise way of writing a test.

Similarly, some applications might be including CaseClass#toString in messages that they display to their users. Any change here, without an escape hatch, could ripple into unexpected problems.

Perhaps an annotation could be used to configure the generated toString. It isn't that much more concise than override def toString = FancyToString(this), though.

@pmpfr
Copy link

pmpfr commented Jul 16, 2018

Great if this were opt-in (and ideally per-field, but that might be too much to ask). You could add a trait FancyToString { final def toString: String = ... } to avoid having to throw this work away in the face of people's legitimate objections to a blanket change.

(Which I agree with: I often feel the lack of "named args" in the built in toString, but I wouldn't want to have them in every case any more than I would want to use named args in every method invocation.)

@dwijnand
Copy link
Member

Please continue the discussion about the idea behind this change in https://contributors.scala-lang.org/t/case-class-tostring-new-behavior-proposal-with-implementation/2056, rather than here.

@olafurpg
Copy link
Contributor

olafurpg commented Jul 17, 2018

@SethTisue
Copy link
Member

SethTisue commented Aug 7, 2018

I reallllllly wish I could be in favor of this but with sadness and regret I lean towards @sjrd's position :-/

at least, I don't think we can simply merge this for 2.13 as-is :-/ :-/ :-/

perhaps we can continue to explore whether there's a way this could happen in stages. e.g. the "opt-back" proposal is interesting.

@SethTisue SethTisue modified the milestones: 2.13.0-M5, 2.13.0-RC1 Aug 7, 2018
@joshlemer
Copy link
Contributor Author

@SethTisue do you think that there's any potential for a ClassicToString style mixin? Something like...

import scala.runtime.ScalaRunTime._toString
trait ClassicToString { this: Product => 
  def toString = _toString(this)
}
case class A(i: Int, b: Int) extends ClassicToString

?

@adriaanm
Copy link
Contributor

adriaanm commented Aug 7, 2018

Thank you for proposing and discussing this! I understand this is desirable, but we can't change this behavior anymore, at least not by default. I hope we can all rally behind #6972 to get that into M5 (I'm +1 on that one). I hope that will provide a good building block for a nicer, opt-in, case class toString, which is clearly on many wishlists.

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

Successfully merging this pull request may close these issues.