case class Point(x: Int, y: Int) {
override def toString: String =
macro ToString.toStringWithNames
}
println(Option(Point(1, 2)))
override def toString = ToString.toStringWithNames[Point].
Not only will this work as expected when you invoke through the superclass method, it will also only need to expand the macro once, rather than at each call site of toString
.
scala> def whereAmIImpl(c: Context): c.Tree= { import c.universe._; q"${c.internal.enclosingOwner.owner.toString}"}
whereAmIImpl: (c: scala.reflect.macros.blackbox.Context)c.Tree
scala> def whereAmI: String = macro whereAmIImpl
defined term macro whereAmI: String
scala> class C { def foo = whereAmI }
defined class C
scala> new C().foo
res3: String = class C
--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
>> To avoid explicitly passing in the type argument, you could inspect the enclosing owner (via a new, power user API in 2.11)I tried to do that at first but that returns the enclosing owner at the call site (a Specification for example) and not the owner of the method (a Point).But there should be a way to access the type at the definition site, right?
IMO the only time you should call this macro to generate a to-stringer for Point
is in the implementation of Point#toString
. Call sites of then just call a regular toString
method.
We should probably warn if you override a method with a macro def. Maybe there are some valid use cases for that, but it is mostly done in error.
-jason
case class Point(x: Int, y: Int) {
override def toString: String = string
def string = macro ToString.toStringWithNames
So you recommend to either:- use a type tag and get the expected type-> that doesn't seem to work when the Point instance is inside an Option- defer to another method like this:case class Point(x: Int, y: Int) {
override def toString: String = string
def string = macro ToString.toStringWithNames}
case class Point(x: Int, y: Int) {
override def toString: String =
macro ToString.toStringWithNames
}
> I'm pretty certain you can rework your macro along the lines of ScalaEquals (or just use that macro!) .I tried ScalaEquals and got the same result. This is not surprising to me because I'm doing something completely similar.
On Fri, Oct 10, 2014 at 10:43 PM, etorreborre <etorr...@gmail.com> wrote:
PS: at least I now understand why my current version behaves as it does. It's not a puzzler anymore :-)
:)
Here’s what I would do:
==> sandbox/macro.scala <==
import scala.reflect.macros.blackbox._
import scala.language.experimental._
object ToString {
def toStringWithNames: String = macro toStringWithNamesImpl
def toStringWithNamesImpl(c: Context): c.Tree = {
import c.universe._
// class for which we want to display toString
val klass = c.internal.enclosingOwner.owner
// we keep the getter fields created by the user
val fields: Iterable[c.Symbol] = klass.asClass.toType.decls
.filter(sym => sym.isMethod && sym.asTerm.isParamAccessor) // we should do more filtering here
// print one field as <name of the field>+"="+fieldName
def printField(field: Symbol) = {
val fieldName = field.name
q"""${fieldName.decoded.toString}+${"="}+this.$field"""
}
val params = fields.foldLeft(q"${""}")((res, acc) => q"${printField(acc)} + $res")
// print the class and all the parameters with their values
q"this.productPrefix + ${"("} + $params + ${")"}"
}
}
==> sandbox/test.scala <==
case class Point(x: Int) {
override def toString = ToString.toStringWithNames
}
object Test extends App {
println(Some(Point(1)).toString)
}
% scalac-hash v2.11.2 sandbox/macro.scala && scalac-hash v2.11.2 sandbox/test.scala && scala-hash v2.11.2 Test
warning: there was one deprecation warning; re-run with -deprecation for details
one warning found
Some(Point(x=1))
-jason