隐式转换适用于符号但不适用于字符串
Implicit conversion works for symbol but not string
我正在为一些基于正式语法的东西开发 DSL。我希望能够说出类似 'start produces "a" andThen 'b andThen "c"
的内容,其中符号和字符串代表语法的不同组成部分。我看到这样的代码有问题:
class ImplicitTest {
trait GrammarPart
case class Nonterminal(name: Symbol) extends GrammarPart
case class Terminal(value: String) extends GrammarPart
case class Wrapper(production: Seq[GrammarPart]) {
def andThen(next: Wrapper) =
Wrapper(production ++ next.production)
}
implicit def symbolToWrapper(symbol: scala.Symbol) =
Wrapper(Seq(Nonterminal(symbol)))
implicit def stringToWrapper(s: String) =
Wrapper(Seq(Terminal(s)))
}
object StringGrammar extends ImplicitTest {
"x" andThen "y" // this causes a compiler error: "value andThen is not a member of String"
}
object SymbolGrammar extends ImplicitTest {
'x andThen "y" // this is fine
}
我的隐式转换似乎对符号工作正常,但是当我尝试将字符串隐式转换为 Wrapper
时,出现编译器错误:"value andThen is not a member of String"。为什么?
由于在 Function
上定义的 andThen
方法,编译器感到困惑。这是一个最小的例子:
class Foo {
def andThen(x: Foo) = ???
implicit def string2foo(s: String): Foo = new Foo
"foo" andThen "bar"
}
编译失败,出现与您的示例相同的错误。尝试将 andThen
重命名为任何其他名称(例如 andThen2
)并查看是否可以编译以说服自己这就是问题所在。
这是正在发生的事情。编译器知道如何通过现有隐式将 String
转换为 Int => Char
:
val f: Int => Char = "foobar"
val g = "foobar" andThen { c => s"character is '$c'" }
g(4) //"character is 'b'"
因为 Function
已经有一个 andThen
方法,这会导致编译器出错。当然,一个完美的编译器可以在这里选择正确的转换,也许它应该根据规范(我没有仔细研究)。但是,您也可以通过提示来帮助它。在您的示例中,您可以尝试:
object StringGrammar extends ImplicitTest {
("x" : Wrapper) andThen "y"
}
您也可以使用不同的方法名称。
另一种验证这是错误的方法是排除隐式 wrapString
,它将 String
转换为 WrappedString
,后者实现 PartialFunction
,从而暴露导致冲突的有问题的 andThen
方法:
//unimport wrapString but import all other Predefs, in order to isolate the problem
import Predef.{wrapString => _, _}
class Foo {
def andThen(x: Foo) = ???
implicit def string2foo(s: String): Foo = new Foo
"foo" andThen "bar"
}
请注意,此技术在 REPL 中不起作用:Predef
取消导入必须在文件中并且是第一次导入。但是上面的代码用 scalac
编译。
当隐式未按预期工作时,有时查看 Predef
中的隐式以查找冲突可能很有用。
我正在为一些基于正式语法的东西开发 DSL。我希望能够说出类似 'start produces "a" andThen 'b andThen "c"
的内容,其中符号和字符串代表语法的不同组成部分。我看到这样的代码有问题:
class ImplicitTest {
trait GrammarPart
case class Nonterminal(name: Symbol) extends GrammarPart
case class Terminal(value: String) extends GrammarPart
case class Wrapper(production: Seq[GrammarPart]) {
def andThen(next: Wrapper) =
Wrapper(production ++ next.production)
}
implicit def symbolToWrapper(symbol: scala.Symbol) =
Wrapper(Seq(Nonterminal(symbol)))
implicit def stringToWrapper(s: String) =
Wrapper(Seq(Terminal(s)))
}
object StringGrammar extends ImplicitTest {
"x" andThen "y" // this causes a compiler error: "value andThen is not a member of String"
}
object SymbolGrammar extends ImplicitTest {
'x andThen "y" // this is fine
}
我的隐式转换似乎对符号工作正常,但是当我尝试将字符串隐式转换为 Wrapper
时,出现编译器错误:"value andThen is not a member of String"。为什么?
由于在 Function
上定义的 andThen
方法,编译器感到困惑。这是一个最小的例子:
class Foo {
def andThen(x: Foo) = ???
implicit def string2foo(s: String): Foo = new Foo
"foo" andThen "bar"
}
编译失败,出现与您的示例相同的错误。尝试将 andThen
重命名为任何其他名称(例如 andThen2
)并查看是否可以编译以说服自己这就是问题所在。
这是正在发生的事情。编译器知道如何通过现有隐式将 String
转换为 Int => Char
:
val f: Int => Char = "foobar"
val g = "foobar" andThen { c => s"character is '$c'" }
g(4) //"character is 'b'"
因为 Function
已经有一个 andThen
方法,这会导致编译器出错。当然,一个完美的编译器可以在这里选择正确的转换,也许它应该根据规范(我没有仔细研究)。但是,您也可以通过提示来帮助它。在您的示例中,您可以尝试:
object StringGrammar extends ImplicitTest {
("x" : Wrapper) andThen "y"
}
您也可以使用不同的方法名称。
另一种验证这是错误的方法是排除隐式 wrapString
,它将 String
转换为 WrappedString
,后者实现 PartialFunction
,从而暴露导致冲突的有问题的 andThen
方法:
//unimport wrapString but import all other Predefs, in order to isolate the problem
import Predef.{wrapString => _, _}
class Foo {
def andThen(x: Foo) = ???
implicit def string2foo(s: String): Foo = new Foo
"foo" andThen "bar"
}
请注意,此技术在 REPL 中不起作用:Predef
取消导入必须在文件中并且是第一次导入。但是上面的代码用 scalac
编译。
当隐式未按预期工作时,有时查看 Predef
中的隐式以查找冲突可能很有用。