Reputation: 434
I'm working on a DSL for some formal grammar based stuff. I'd like to be able to say something like 'start produces "a" andThen 'b andThen "c"
, where symbols and strings represent different components of the grammar. I'm seeing a problem with code like this:
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
}
It seems my implicit conversion works fine for a symbol, but when I try to implicitly convert a string to a Wrapper
, I get a compiler error: "value andThen is not a member of String". Why?
Upvotes: 3
Views: 299
Reputation: 16324
The compiler is getting confused because of the andThen
method that is defined on Function
. Here is a minimal example:
class Foo {
def andThen(x: Foo) = ???
implicit def string2foo(s: String): Foo = new Foo
"foo" andThen "bar"
}
This fails to compile with the same error as your example. Try renaming andThen
to anything else (e.g. andThen2
) and see that this compiles in order to convince yourself that this is the problem.
Here's what's going on. The compiler knows how to convert String
to Int => Char
through existing implicits:
val f: Int => Char = "foobar"
val g = "foobar" andThen { c => s"character is '$c'" }
g(4) //"character is 'b'"
Since Function
already has an andThen
method, this is tripping up the compiler. Of course, a perfect compiler could feasibly choose the correct conversion here, and maybe it should according to the spec (I haven't looked into it too carefully). However, you can also just help it along with a hint. In your example, you might try:
object StringGrammar extends ImplicitTest {
("x" : Wrapper) andThen "y"
}
You could also just use a different method name.
Another way to verify that this is the error is to exclude the implicit wrapString
, which converts String
to WrappedString
, which implements PartialFunction
and thereby exposes the problematic andThen
method that is causing the conflict:
//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"
}
Note that this technique doesn't work in the REPL: Predef
un-importing has to be in a file and the first import. But the above code compiles with scalac
, for example.
When implicits aren't working as expected, it can sometimes be useful to look at the implicits in Predef
for conflicts.
Upvotes: 6