Подтвердить что ты не робот

Различие между типами псевдонимов и типа лямбда

Этот вопрос касается ограничения системы неявного разрешения Scala, которую я использовал несколько раз при использовании Scalaz, и это не имеет большого смысла для меня. Я переделал эту проблему в версию, отличную от Scalaz, ниже, но я рад предоставить дополнительную информацию о мотивации, если это необходимо.

Предположим, что у меня есть пара классов типов, которые что-то свидетельствуют о конструкторе типа:

import scala.language.higherKinds

trait Foo[F[_]]
trait Bar[F[_], A]

Теперь также предположим, что если у меня есть экземпляр Foo для некоторого F, я знаю, что у меня также есть экземпляр Foo для Bar[F, _]:

implicit def barFoo[F[_]: Foo] = new Foo[({type L[X] = Bar[F, X]})#L] {}

У меня также есть экземпляры для List и правой части Either:

implicit object listFoo extends Foo[List]
implicit def eitherFoo[A] = new Foo[({type L[X] = Either[A, X]})#L] {}

Теперь довольно ясно, что я должен был бы написать следующее:

type BarList[X] = Bar[List, X]

implicitly[Foo[BarList]]

Или, что эквивалентно:

implicitly[Foo[({type L[X] = Bar[List, X]})#L]]

И действительно, обе работают точно так, как ожидалось.

Итак, я пробую следующее:

type StringOr[X] = Either[String, X]
type BarStringOr[X] = Bar[StringOr, X]

И затем:

scala> implicitly[Foo[BarStringOr]]
res2: Foo[BarStringOr] = [email protected]

Опять же, никаких сюрпризов здесь нет. Но затем я пытаюсь:

implicitly[Foo[({type L[X] = Bar[StringOr, X]})#L]]

И я получаю следующее:

<console>:15: error: could not find implicit value for parameter e: Foo[[X]Bar[[X]scala.util.Either[String,X],X]]
              implicitly[Foo[({type L[X] = Bar[StringOr, X]})#L]]
                        ^

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

scala> implicitly[Foo[StringOr]]
res4: Foo[StringOr] = [email protected]

scala> barFoo[StringOr]
res5: Foo[[X]Bar[StringOr,X]] = [email protected]

У меня возникли проблемы с определением того, какое важное различие может быть между случаями List и StringOr, которые позволяют использовать версию типа лямбда для первой, но не последней.

Я пробовал это на Scala 2.10.0-RC5 и 2.9.2. Добавление ковариации по всему не помогает.

Я пропустил что-то очевидное? Может ли кто-нибудь указать мне на что-то в спецификации, которая поможет мне понять это или предыдущие обсуждения подобных проблем?

4b9b3361

Ответ 1

Хорошо, я не уверен на 100%, но я думаю, что мы сможем добиться определенного прогресса, сократив это до самого простого возможного случая, который терпит неудачу. Implicits здесь не проблема, а также псевдонимы типов. Этого достаточно, чтобы потерпеть неудачу:

trait Foo[F[_]]
trait Bar[F[_], A]

def barFoo[F[_]: Foo] = new Foo[({type L[X] = Bar[F, X]})#L] {}

val res1: Foo[({type L[X] = Either[String, X]})#L] = null

val works = barFoo[({type L[X] = Either[String, X]})#L](res1)
val fails = barFoo(res1)

Проблема заключается в недействительности Scala, чтобы вывести аргумент типа для barFoo как [X]Either[java.lang.String,X]. По-видимому, это связано с (или, по крайней мере, связанным) с отказом скаляра, чтобы вывести частично примененный конструктор типов.

(В соответствующей заметке это пример одного из тех типов, которые Scala считает неприемлемо сложным).