2016-11-03 3 views
2

Ich mag wäre ziemlich Druck auf ein Product, wie ein case class, so dass ich erstellen Sie das folgende Merkmal:dynamischen String-Interpolation

trait X extends Product { 
    def fmtStrs = 
     productIterator map { 
     case _ : Double => "%8.2f" 
     case _ => "%4s" 
     } map (_ + separator) toSeq 
    override def toString = { 
     new StringContext("" +: fmtStrs : _*) f (productIterator.toSeq : _*) 
    } 
    } 

Diese verwendet String-Interpolation, wie in der ScalaDoc für StringContext beschrieben.

Aber dies wird nicht kompiliert, mit diesem kryptischen Fehler:

Error:(69, 70) too many arguments for interpolated string 
     new StringContext("" +: fmtStrs : _*) f (productIterator.toSeq : _*) 

Ist das ein Bug oder Einschränkung eines Makros? Beachten Sie, dass die folgenden funktioniert gut tun, so dass ich vermute, dies ist auf die Variable Argumentliste in Beziehung gesetzt werden kann:

scala> val str2 = StringContext("","%4s,","%8.2f").f(1,23.4) 
str2: String = " 1, 23.40" 
+0

Eher bizarr, wenn Sie den "f" Interpolator in den "s" Interpolator ändern, scheint dies zu funktionieren (und funktioniert) ... – Luciano

Antwort

2

Der Grund f ist ein Makro, so dass es ein Fehler geben kann, wenn Arten von Formatbezeich und Argumente nicht übereinstimmen, und dies ist nicht möglich, indem Sie auf ("" +: fmtStrs : _*) und (productIterator.toSeq : _*) betrachten, so ist es nicht besonders überraschend, dass dies nicht funktioniert. Die Fehlermeldung könnte klarer sein, also lass uns sehen, was genau passiert. (Schließlich hat für die Fehlermeldung durch die Suche es einige Zeit, es tatsächlich nahm zu finden, I), werden Sie

c.macroApplication match { 
    //case q"$_(..$parts).f(..$args)" => 
    case Applied(Select(Apply(_, parts), _), _, argss) => 
    val args = argss.flatten 
    def badlyInvoked = (parts.length != args.length + 1) && truly { 
     def because(s: String) = s"too $s arguments for interpolated string" 
     val (p, msg) = 
     if (parts.length == 0) (c.prefix.tree.pos, "there are no parts") 
     else if (args.length + 1 < parts.length) 
      (if (args.isEmpty) c.enclosingPosition else args.last.pos, because("few")) 
     else (args(parts.length-1).pos, because("many")) 
     c.abort(p, msg) 
    } 
    if (badlyInvoked) c.macroApplication else interpolated(parts, args) 

Mit Ihrem Anruf zu sehen haben Sie einen einzigen Baum

Wenn Sie the implementation of f schauen in beiden parts und argss, und parts.length != args.length + 1 ist wahr, so ist badlyInvoked wahr.

s ist es egal, wie seine Argumente aussehen, also ist es nur eine Methode und Ihr Szenario funktioniert.

+0

Danke; für den Datensatz, endete am Ende 'java.lang.String.Formatter' direkt anstelle der Verwendung von String-Interpolation. – Luciano