在Scala中键入安全字符串插值

时间:2014-05-06 21:04:48

标签: scala type-safety scala-macros string-interpolation scala-macro-paradise

this的启发,我想知道我们是否可以在Scala中使用类型安全的字符串插值(可能使用宏)?

例如,我希望有这样的东西

def a[A] = ???
val greetFormat = f"Hi! My name is ${a[String]}. I am ${a[Int]} years old" 
greetFormat.format("Rick", 27)  // compiles
//greetFormat.format("Rick", false)  // does not compile
//greetFormat.format(27, "Rick") // does not compile
//greetFormat.format("Rick", 27, false) // does not compile
//greetFormat.format("Rick") // does not compile or is curried?

3 个答案:

答案 0 :(得分:3)

f字符串插值器已经用宏实现。

这可以在REPL中证明:

scala> val b = "not a number"
b: String = not a number

scala> f"$b%02d"
<console>:9: error: type mismatch;
 found   : String
 required: Int
              f"$b%02d"
                 ^

答案 1 :(得分:2)

将其包装在一个函数中。

def greet(name: String, age: Int) = s"Hi! My name is $name.  I am $age years old"

答案 2 :(得分:1)

您可以为f-interpolator提供含义:

scala> case class A(i: Int)
defined class A

scala> implicit def atoi(a: A): Int = a.i
warning: there were 1 feature warning(s); re-run with -feature for details
atoi: (a: A)Int

scala> f"${A(42)}%02d"
res5: String = 42

另请参阅Travis Brown's examplesusing regex group names in extractions的解决方案。我花了大约一分钟才能窃取这个好主意。

"a123bc" match {
  case res @ xr"(?<c>a)(?<n>\d+)(?<s>bc)" => assert {
    res.c == 'a' && res.n == 123 && res.s == "bc"
  }
}

为了记录,在构图方面,我想:

val a = A(Rick, 42)
val greeter = f"Hi! My name is $_. I am ${_}%d years old" 
greeter(a, a)

但对于可怜的下划线来说,这被认为太多了。您必须像在另一个答案中一样编写该函数。

您的表单,您的宏看到"${a[Int]}"并使用Int参数编写函数,并不难以实现。

f-interpolator的其他功能包括其他静态错误检查:

scala> f"$b%.02d"
<console>:19: error: precision not allowed
              f"$b%.02d"
                   ^

并支持Formattable

scala> val ff = new Formattable { def formatTo(fmtr: Formatter, flags: Int, width: Int, precision: Int) = fmtr.format("%s","hello, world") }
ff: java.util.Formattable = $anon$1@d2e6b0b

scala> f"$ff"
res6: String = hello, world

快速宏可能会发出(i: Int) => f"${ new Formattable {...} }"