1

我正在尝试String使用一种新的 apply 方法进行扩展,该方法让我可以在其上应用高阶函数。例子:

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 中转换StringStringOps.

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}
   ^

为什么它寻找一个通用的应用方法(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的结果类型中寻找类型参数。我让它工作,但函数参数和应用方法的场景不起作用。怎么来的?

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
4

3 回答 3

2

当你写这个:

"foo"{f}

编译器会将其翻译为:

"foo".apply { f }

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

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 反射发生的,这会使其效率低下。

于 2011-05-06T08:24:46.713 回答
1

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

于 2011-05-06T09:35:23.693 回答
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}

另请参阅:有没有办法控制默认使用的隐式转换?

于 2011-05-06T12:08:43.083 回答