2016-11-03 6 views
2

Я хотел бы довольно-Распечатайте Product, такие как case class, поэтому я создаю следующую особенность:динамическая строка интерполяции

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 : _*) 
    } 
    } 

Это использует интерполяцию строки, как описано в ScalaDoc для StringContext.

Но это не составит, с этой загадочной ошибки:

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

Является ли это ошибка или ограничение макроса? Обратите внимание, что выполнение следующего работает хорошо, так что я подозреваю, что это может быть связанно с переменным списком аргументов:

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

Скорее странно, если вы измените интерполятор 'f' на интерполятор' s', это, по-видимому, соответствует (и работает) ... – Luciano

ответ

2

Причина f макрос так, что он может дать вам ошибку, когда типы спецификаторов формата и аргументов не соответствуют, и это невозможно проверить, посмотрев на ("" +: fmtStrs : _*) и (productIterator.toSeq : _*), так что это не удивительно, что это не работает. Сообщение об ошибке может быть более четким, поэтому давайте посмотрим, что именно происходит.

Если посмотреть на the implementation of f (это мне потребовалось некоторое время, чтобы на самом деле найти его, я, наконец, сделал путем поиска сообщения об ошибке), вы увидите

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) 

С вашего звонка у вас есть одно дерево в parts и argss, и parts.length != args.length + 1 - это правда, поэтому badlyInvoked - это правда.

s все равно, как выглядят его аргументы, поэтому это всего лишь метод, и ваш сценарий работает.

+0

Спасибо; для записи, просто закончил вызов 'java.lang.String.Formatter' непосредственно вместо использования интерполяции строк. – Luciano