类型别名和类型lambdas之间的区别

时间:2012-12-21 20:11:22

标签: scala types scalaz implicits

这个问题是关于Scala的隐式解析系统的局限性,在使用Scalaz时我遇到过几次,这对我来说没有多大意义。我已将问题提炼到下面的Scalaz-less版本,但我很乐意在需要时提供有关动机的更多信息。

假设我有几个类型类可以见证类型构造函数:

import scala.language.higherKinds

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

现在还假设如果某个FooF个实例,我知道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

我无法确定ListStringOr案例之间可能存在哪些重要区别,这些案例允许类型lambda版本适用于前者但不适用于后者。

我在Scala 2.10.0-RC5和2.9.2上尝试过这个。在整个过程中添加协方差无济于事。

我错过了一些明显的东西吗?有人能指出我在规范中的某些内容可以帮助我理解这个问题,还是先前对类似问题的讨论?

1 个答案:

答案 0 :(得分:4)

好的,我不是百分百肯定,但我认为我们可以通过将此减少到最简单的失败案例来取得一些进展。 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]。这似乎是由scalac's refusal to infer a partially applied type constructor引起的(或至少与之相关)。

(在相关的说明中,这是Scala认为不可接受的复杂类型之一的示例。)