Scala中隐式转换和高阶应用方法的问题

时间:2011-05-06 08:07:03

标签: scala implicit-conversion higher-order-functions

我正在尝试使用新的apply方法扩展String,该方法允许我在其上应用更高阶的函数。例如:

case class A(s:String, f: List[String] => List[String])

val f: List[String] => List[String] = { ... stuff ... }
"foo"{f} // == A("foo", f)

所以我已经定义了一个从String到一个带有List[String] => List[String]函数的apply方法的隐式转换。

implicit def c(s:String) = new {
    def apply(f: List[String] => List[String]) = A(s, f)
}

但是当我尝试使用它时,转换会与Predef中的转换相冲突,将String转换为StringOps

scala> "foo"{f}                                                
<console>:19: error: type mismatch;
 found   : java.lang.String
 required: ?{val apply: ?}
Note that implicit conversions are not applicable because they are ambiguous:
 both method c in object $iw of type (s: String)java.lang.Object{def apply(f: (List[String]) => List[String]): A}
 and method augmentString in object Predef of type (x: String)scala.collection.immutable.StringOps
 are possible conversion functions from java.lang.String to ?{val apply: ?}
       "foo"{f}
   ^

为什么它会寻找一个通用的apply方法(required: ?{val apply: ?}),而不是一个接受我的类型参数(List[String] => List[String])的方法?

编辑:

我通过避免使用裸字符串来表达变量来解决这个问题(在我正在研究github的项目中)。所以现在它看起来像这样:

case class param(val v: String) {
    def apply(f: Emit.Selector) = Variable(v, f)
}

val $foo = param("foo")
foo{selector} // works fine

我不需要使用暗示。

进一步更新

scala确实在搜索时在implicits的结果类型中查找类型参数。我得到这个工作,但使用函数参数和apply方法的方案不起作用。怎么样?

scala> class A()
defined class A

scala> class B()
defined class B

scala> implicit def one(s:String) = new {
     |   def a(a:A) = s + " A"
     | }
one: (s: String)java.lang.Object{def a(a: A): java.lang.String}

scala> implicit def another(s:String) = new {
     |   def a(b:B) = s + " B"
     | }
another: (s: String)java.lang.Object{def a(b: B): java.lang.String}

scala> "hello" a new A
res1: java.lang.String = hello A

scala> "hello" a new B
res2: java.lang.String = hello B

3 个答案:

答案 0 :(得分:2)

当你这样写:

"foo"{f}

编译器会将其翻译为:

"foo".apply { f }

更一般地说:applyupdate是编译器中存在语法糖的两种特殊方法:

obj(arg)         // gets translated to:  obj.apply(arg)
obj(index) = arg // gets translated to:  obj.update(index, arg)

StringOps已经提供了apply(index: Int),并且在寻找隐式转换时,编译器会查找最后提供名为apply的成员的第一个(无论参数如何)。在你的情况下,你有冲突。

也许您可以将方法重命名为

"foo" useFor { f }

顺便说一下:总是声明隐式转换的返回类型是一种好习惯。此外,您需要在性能关键的情况下避免new { def apply /* ... */ }样式,因为随后对apply的任何调用都会通过Java反射进行,这会使其效率低下。

答案 1 :(得分:1)

您可以通过将-Yno-imports传递给scalac来“禁用”所有标准导入(以及所有标准隐含)(不适用于repl。)。这样可以避免冲突,但是您必须明确导入您使用的所有内容。

答案 2 :(得分:1)

您可以将转换从c更改为augmentString以便遮蔽它,但之后只会找到另一个较低优先级的转换(wrapString)。但是,如果您同时隐藏augmentString并将其添加到LowPriorityImplicits的扩展名中:

object HighPriorityImplicits extends LowPriorityImplicits {
  implicit def augmentString(s:String) = new { 
    def apply(f: List[String] => List[String]) = A(s, f) 
  }
}

然后它应该工作:

import HighPriorityImplicits._
"foo"{f}

另请参阅:Is there a way to control which implicit conversion will be the default used?