-
Notifications
You must be signed in to change notification settings - Fork 21
Poor interaction between ScalaRunTime.stringOf and Interpreter #3710
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
Imported From: https://issues.scala-lang.org/browse/SI-3710?orig=1
|
@paulp said: // Given:
List(Array(1))
// How to print as above and not as:
List([I@18f55759) 100% of the logic spins out of the inability to change Array's toString method. It does not arise from some high-minded attempt to improve everybody's toString method. Your "HasStringRepr" idea is haskell's Show typeclass. It would be my preference to use Show (and Eq) in preference to universal equals and toString wherever possible, but there is not general agreement on this point and there are performance implications. |
Daniel Ramage (dramage) said:
I appreciate the clarification. It did occur to me that this method would also be used recursively. So if the main motivation is really to fix java arrays' toString, why not go with proposal 1? Is there a reason that stringOf should avoid x.toString except on null and on java arrays? It seems like the only other branch that doesn't call x.toString is for Traversable with definite size not in scala.tools.nsc.io. Something's fishy ... why have this branch instead of just implementing a default, desirable toString on Traversable?
There's certainly complexity there, so I won't push for the typeclass way of doing things in the near term (but I'd love it in the long term). I'd imagine that one such scenario where a Show style typeclass isn't desirable is when the static type signature is a supertype of the runtime type, which has its own Show. Statically, the supertype's Show would be used instead of the subtype's, which seems like it might not be the desired behavior. |
Daniel Ramage (dramage) said:
A bit more concretely, why not change Traversable's toString (which ultimately delegates to TraversableOnce.addString) to use ScalaRunTime.stringOf(x) instead of x.toString? |
@paulp said:
That's up to martin. For better or worse the "official" way to stringify objects in scala is .toString. The repl is a piece of software we ship and we can reasonably improve on that behavior in that context. Changing how Traversable goes about it is a more far-reaching change. If we were going to do it I'd hope to do a lot better than catching NPEs and improving how Array is printed, not that those aren't worthwhile things. A simple backward compatible approach would be to define a new method like toScalaString with a default implementation which calls toString, maybe even taking some useful implicit argument with an empty default. But so far martin hasn't been too enthusiastic about such proposals. |
Daniel Ramage (dramage) said:
So it seems like there are really two things going on. One is a bug in the REPL: the "official" way to stringify an object in scala is not used by the REPL for types that extend Traversable. Perhaps this ticket should be re-filed as a defect rather than enhancement, accordingly? At the same time, we're starting a discussing on something that's more of a feature or enhancement, which is how to fix toString in some more general way. While that would be great, I don't think we should have to do it before we fix the REPL.
This would be a method on Traversable? Seems like adding a new method (and thereby introducing more API that might change) might be a bigger deal than changing Traversable's toString to properly handle nulls and arrays ... dan |
@paulp said:
We don't. That's why the ticket is still open.
The question is bigger than Traversable. This is like doing null checks in some subset of dereferences. You do a little of it and it becomes an endless series of why here and not there. Whatever the mechanism for stringifying an object is to be, we should adhere to it consistently. |
@lrytz said: |
Daniel Ramage (dramage) said: Thanks for discussing this at the meeting. I'm confused by the wontfix resolution, though, because this ticket has two parts, one is a enhancement and the other is a defect. The enhancement under discussion involves changing Traversable's toString, and it's fine that the answer there is wontfix, because it will be tricky for all the reasons outlined above and presumably for other reasons discussed at the meeting. But the main reason for this ticket is that the way the REPL deals with Traversables is broken. Currently, the REPL uses ScalaRunTime.stringOf, which selectively avoids calling toString for anything that extends Traversable. So scala's interpreter loop ignores scala's official mechanism for turning objects into strings. This just has to be considered a defect, no? |
@paulp said: |
@lrytz said: |
@paulp said:
No, I wasn't suggesting we stop using stringOf, only that we take a harm minimization strategy in so doing. |
Alex McGuire (cage433) said: |
Alex McGuire (cage433) said: I think choice 2 would fix this. For now I think I can work around the problem by getting my DateRange class to extend IterableView. I've attached a minimal case. attachment:ticket:3710:DateRange.scala Running 'scala test.Day' causes the stack overflow |
@paulp said: |
@paulp said: |
Daniel Ramage (dramage) said: |
Hi folks,
Since some time in the 2.8.0 branch, the interpreter started using ScalaRunTime.stringOf to show string values instead of calling objects' toString methods. It seems the motivation here is for stringOf to construct a python-repr-like string for an object at runtime, a noble goal.
Unfortunately, the current implementation of ScalaRunTime.stringOf falls short for custom collections. An elaborate instance check is performed to determine how to construct the string with several special cases that apply only to built in collections, with no hooks for external collections that extend Traversable to provide an alternative. The code block in question from the current head branch is attached, below, for reference.
More specifically, custom Traversable instances with definite size that are not part of scala.tools.nsc.io cannot provide a custom string. This interacts poorly with the interpreter for, e.g., all the numerical collections in scalala http://github.com/scalala/Scalala, for which appropriate toString methods exists. The custom toString methods on those collections are concise and readable, but the stringOf version are often useless and/or slow. The result is that the interpreter moves from being a useful matlab-like environment to something far less useful for many real interactions.
It seems like there are a few choices here:
Make toString behave correctly for all scala data types, so that stringOf just does the check for isArray (because java arrays' toStrings cannot be changed), but otherwise delegates to x.toString.
If there really were a use case for having stringOf and toString look different, instead define a HasStringRepr trait that defines a .toStringRepr method. Let Traversable extends this. Then stringOf can defer to x.toStringRepr if the class implements HasStringRepr, or x.toString otherwise unless it isArray.
Use a HasStringRepr view class, i.e.
def stringOf[A:HasStringRepr](arg : A)
. This options seems the most flexible, as it will allow a custom string repr for Arrays, but requires more complexity in the Interpreter loop, because it can't easily be done by reflection.1 and 2 seem easy enough work-arounds for the near term. I think 3 is the way to go, but there is some complexity to it. I've worked out the details for doing this more generally here: http://github.com/scalanlp/scalanlp-core/blob/master/data/src/main/scala/scalanlp/serialization/TextSerialization.scala
The text was updated successfully, but these errors were encountered: