2016-02-27 3 views
8

У меня возникла странная ошибка компилятора о неявной, которая на самом деле присутствует, но не может быть найден по какой-либо причине. Поэтому я создал небольшой тестовый пример, который воспроизводит таинственное поведение.Почему в некоторых случаях игнорируются случаи высокого порядка?

trait Hide { 
    type T 
} 
object HideString extends Hide { 
    override type T = String 
} 
object HideBool extends Hide { 
    override type T = Boolean 
} 

Простой тип используется как однозначная цель для неявных преобразований.

def id[H <: Hide, C](x : C)(implicit ev : C => H#T) : H#T = ev(x) 
def drop[H <: Hide, C](x : C)(implicit ev : C => H#T) : Int = { 
    println(ev(x)) 
    1 
} 
def idSeq[H <: Hide, C](x : Seq[C])(implicit ev : Seq[C] => Seq[H#T]) : Seq[H#T] = ev(x) 
def dropSeq[H <: Hide, C](x : Seq[C])(implicit ev : Seq[C] => Seq[H#T]) : Int = { 
    println(ev(x)) 
    1 
} 

Методы, основанные на неявных преобразованиях. Это в основном матрица 2x2. id методы возвращают преобразованный тип и методы drop используют преобразование внутри и возвращают некоторую константу. Нормальные методы действуют на точный неявно преобразованный тип, а методы Seq работают с последовательностями.

implicit def exString(x : String) : HideString.type#T = x 
implicit def highString[F[_]](x : F[String]) : F[HideString.type#T] = x 

Над неявных преобразований highString определяется с типом высшего порядка.

val s1 = id("sdf") 
val s2 = drop("aero") 

val r1 = idSeq(Seq("a", "bc")) 
val r2 = dropSeq(Seq("i", "IO")) 

Попытка фактически использовать преобразования приносит мне ошибку:

ImplicitResolution.scala:98: error: No implicit view available from Seq[String] => Seq[test.implicits.HighReduction.Hide#T]. 
    val r2 = dropSeq(Seq("i", "IO")) 

Это может быть суммированы в следующей матрице:

|  | id | drop | 
|--------+------+------| 
| normal | pass | pass | 
| seq | pass | fail | 

Если я использую точно определенную неявное преобразование для dropSeq метода он обычно встречается:

implicit def seqBool(x : Seq[Boolean]) : Seq[HideBool.type#T] = x 

val a1 = idSeq(Seq(true, false)) 
val a2 = dropSeq(Seq(false, true)) 

И кроме того, если я явно указать неявный аргумент dropSeq начал работать:

val r2i = dropSeq(Seq("i", "IO"))(highString[Seq] _) 

И это самое странное. highString неявно соответствует всем требованиям. И объявлено как implicit, поэтому он должен быть найден компилятором. А в случае idSeq он действительно найден. Итак, почему это проигнорировано в случае с dropSeq?

ответ

1

В вашем случае единственная разница между idSeq и dropSeq - это тип возврата: вы попали в какой-то угловой футляр в компиляторе Scala, который стоит сигнализировать сообществу Scala.

говорится, ваш idSeq имеет неправильную подпись: H#X не означает, что тип X для указанного типа H, а X для любого экземпляра H (не тот, который был разрешен компилятором, см Daniel Sobral объяснение здесь What does the `#` operator mean in Scala?)

То, что вы, вероятно, хотите сделать, это установить связь между Н и вашим типом результата, который легче, если ввести псевдонимы типа, чтобы получить более читаемую подпись:

object Hide { 
    type HideAux[X] = Hide { type T = X} 
} 

вы можете затем перепишите код, например, t его:

def idSeq[B,H <: HideAux[B], C](x : Seq[C])(implicit ev : Seq[C] => Seq[B]) : Seq[B] = ev(x) 
    def dropSeq[B,H <: HideAux[B], C](x : Seq[C])(implicit ev : Seq[C] => Seq[B]) : Int = { 
    println(ev(x)) 
    1 
    } 

Этот код компилируется, и заметьте также, что если вы правильно использовать общие и классы типов, вы не будете нуждаться в двух различных методов id и idSeq, потому что динамическое поведение будет обеспечиваться самим класса типов.

+2

Я был бы более склонен наградить вас щедростью, если бы вы представили проблему для «углового случая в компиляторе Scala», который вы описываете. Я предположил, что это был какой-то угловой случай, но вы все еще не предоставили достаточной информации для меня, чтобы узнать, является ли это ожидаемым * поведением, или если это действительно ошибка в компиляторе. Было бы также неплохо, если бы вы избавились от этого болтающегося '}' ... – DaoWen

+0

Ваши две функции имеют одну и ту же подпись, кроме типа возврата, но вы используете вывод типа для r1 и r2 (поэтому тип возврата не может использоваться для неявного разрешения). Если можно разрешить неявное для r1 с доступной информацией о типе, можно разрешить неявное для r2: учитывая, что функция drop works и dropSeq этого не делает, вполне вероятно, что проблема связана с общими типами и типами. – Edmondo1984

+0

Использование '-Xlog-implicits' дает некоторые интересные ошибки. –