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

Този въпрос е за ограничение на имплицитната система за разделителна способност на 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] = $anon$1@39a6c855

Тук отново няма изненади. Но след това опитвам:

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] = $anon$1@3eaac006

scala> barFoo[StringOr]
res5: Foo[[X]Bar[StringOr,X]] = $anon$1@179fbfea

Имам проблем да идентифицирам каква важна разлика може да има между случаите List и StringOr, която позволява на версията тип ламбда да работи за първия, но не и за втория.

Опитах това на Scala 2.10.0-RC5 и 2.9.2. Добавянето на ковариация навсякъде не помага.

Пропускам ли нещо очевидно? Може ли някой да ме насочи към нещо в спецификацията, което би ми помогнало да разбера това, или към предишни дискусии на подобни проблеми?


person Travis Brown    schedule 21.12.2012    source източник
comment
Не съм сигурен по тази тема, но изглежда има нещо общо с проблеми .scala-lang.org/browse/SI-2712   -  person aemxdp    schedule 22.12.2012
comment
@andriyp: Благодаря, но не съм сигурен, че виждам как това е свързано.   -  person Travis Brown    schedule 22.12.2012
comment
Травис също отвори билет за това на issues.scala-lang.org/browse/SI- 6895   -  person Seth Tisue    schedule 19.06.2015


Отговори (1)


Добре, не съм 100% сигурен, но мисля, че можем да постигнем известен напредък, като сведем това до най-простия възможен случай, който се проваля. Имплицитните не са проблемът тук, нито псевдонимите на типове. Това е достатъчно за провал:

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]. Това изглежда се дължи (или поне е свързано с) на отказът на scalac да изведе частично приложен тип конструктор.

(Във връзка с това, това е пример за един от тези типове, които Scala смята за неприемливо сложни).

person Owen    schedule 15.01.2013