Проблема с неявными преобразованиями и методами применения высшего порядка в Scala - PullRequest
1 голос
/ 06 мая 2011

Я пытаюсь расширить 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 во что-то с помощью метода apply, который принимает функцию List[String] => List[String].

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

Почему он ищет универсальный метод применения (required: ?{val apply: ?}), а не метод, который принимает аргумент моего типа (List[String] => List[String])?

Edit:

Я решил это, воздерживаясь от использования пустых строк для выражения переменных (в проекте я работаю над github ). Итак, теперь это выглядит так:

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

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

И мне не нужно использовать импликации.

Дальнейшее обновление

Похоже, что при поиске scala ищет параметры типа в результирующих типах последствий. Я заставляю это работать, но сценарий с параметром функции и методом 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 ]

2 голосов
/ 06 мая 2011

Когда вы пишете это:

"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, что делает его неэффективным.

1 голос
/ 06 мая 2011

Вы можете изменить преобразование с 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}

См. Также: Есть ли способконтроль, какое неявное преобразование будет использоваться по умолчанию?

1 голос
/ 06 мая 2011

Вы можете «отключить» все стандартные импорты (и, следовательно, все стандартные импликации), передав -Yno-imports в scalac (не работает с репликой). Это позволит избежать конфликта, но тогда вам придется явно импортировать все, что вы используете.

...