【问题标题】:Path-Dependent type inside class value in ScalaScala中类值内的路径依赖类型
【发布时间】:2013-04-17 07:27:53
【问题描述】:

我想将具有抽象类型的类型的值赋予一个类,然后使用它的路径相关类型。看下面的例子(使用 Scala 2.10.1):

trait Foo {
  type A
  def makeA: A
  def useA(a: A): Unit
}

object Test {

  class IntFoo extends Foo {
    type A = Int
    def makeA = 1
    def useA(a: Int) = println(a)
  }

  class FooWrap(val a: Foo) {
    def wrapUse(v: a.A) = a.useA(v)
  }

  val foo = new IntFoo

  /* Path dependent locally */
  val bar = foo  
  bar.useA(foo.makeA)   // works

  /* Path dependent through class value */
  val fooWrap = new FooWrap(foo)

  fooWrap.a.useA(foo.makeA)  // fails
  // error: type mismatch; found : Int required: Test.fooWrap.a.A

  fooWrap.wrapUse(foo.makeA) // fails
  // error: type mismatch; found : Int required: Test.fooWrap.a.A

}

首先,我不明白本地和类值情况之间的根本区别(注意公共的、不可变的值)以及类型检查失败的原因(因为显然Test.fooWrap.a.A =:= foo.A)。这是 Scala 编译器的限制吗?

其次,我怎样才能实现我想要做的事情?

更新

这似乎可以通过使用泛型和内联类型约束来实现:

class FooWrap[T](val a: Foo { type A = T }) {
  def wrapUse(v: T) = a.useA(v)
}

但是,在我的例子中,A 实际上是一个更高种类的类型,所以示例变为:

trait Foo {
  type A[T]
  def makeA[T]: A[T]
  def useA(a: A[_]): Unit
}

object Test {

  class OptFoo extends Foo {
    type A[T] = Option[T]
    def makeA[T] = None
    def useA(a: A[_]) = println(a.get)
  }

  class FooWrap(val a: Foo) {
    def wrapUse(v: a.A[_]) = a.useA(v)
  }

  val foo = new OptFoo

  /* Path dependent locally (snip) */

  /* Path dependent through class value */
  val fooWrap = new FooWrap(foo)

  fooWrap.a.useA(foo.makeA)  // fails
  // polymorphic expression cannot be instantiated to expected type;
  // found : [T]None.type required: Test.fooWrap.a.A[_]

  fooWrap.wrapUse(foo.makeA) // fails
  // polymorphic expression cannot be instantiated to expected type;
  // found : [T]None.type required: Test.fooWrap.a.A[_]

}

【问题讨论】:

标签: scala path-dependent-type


【解决方案1】:

在您最初的问题中,您的问题是 Scala 编译器无法证明 foo.makeA 的结果类型与 fooWrap.a.useA 的参数类型相等。为此,它需要能够证明foofooWrap.a 的身份,我们可以直观地看到这里肯定是这种情况,但这对于编译器来说并不容易跟踪。

有几种方法可以解决这个问题。首先,您可以统一使用fooWrap.a 代替foo

scala> fooWrap.a.useA(fooWrap.a.makeA)
1

现在编译器很容易将 A (fooWrap.a) 的前缀识别为在两次出现中相同。

其次,您可以对FooWrap 进行参数化,以更精确地捕获其Foo 参数的类型,

scala> class FooWrap[F <: Foo](val a: F) {
     |   def wrapUse(v: a.A) = a.useA(v)
     | }
defined class FooWrap

scala> val fooWrap = new FooWrap(foo)
fooWrap: FooWrap[IntFoo] = FooWrap@6d935671

scala> fooWrap.a.useA(foo.makeA)
1

这里FooWrap 的类型参数被推断为IntFoo 而不是裸Foo,因此A 被认为是Int,因为它在foo.makeA 的结果类型中。

在您的更新中,您引入了一个额外的问题:您将useA 的签名更改为,

def useA(a: A[_]): Unit

这里的_ 是一个存在主义,它将挫败所有试图哄骗编译器证明有用的类型等式的尝试。相反,您需要类似的东西,

trait Foo {
  type A[T]
  def makeA[T]: A[T]
  def useA[T](a: A[T]): Unit
}

class OptFoo extends Foo {
  type A[T] = Option[T]
  def makeA[T]: A[T] = None
  def useA[T](a: A[T]) = a map println
}

class FooWrap[F <: Foo](val a: F) {
  def wrapUse[T](v: a.A[T]) = a.useA(v)
}

val foo = new OptFoo

REPL 会话示例,

scala> val fooWrap = new FooWrap(foo)
fooWrap: FooWrap[OptFoo] = FooWrap@fcc10a7

scala> fooWrap.a.useA(foo.makeA)

scala>

【讨论】:

    【解决方案2】:

    更高种类的类型也可以作为泛型参数添加到 FooWrap:

    class FooWrap[T[V]](val a: Foo { type A[V] = T[V] }) {
      def wrapUse(v: T[_]) = a.useA(v)
    }
    

    但是(在这个例子中)推理失败了:

    val fooWrap = new FooWrap[Option](foo)
    

    否则:

    - type mismatch; found : Test.foo.type (with underlying type Test.OptFoo) required: Foo{type A[V] = T[V]}
    - inferred kinds of the type arguments (Option[V]) do not conform to the expected kinds of the type parameters (type T) in class FooWrap. Option[V]'s type parameters do not match type T's expected 
     parameters: class Option has one type parameter, but type T has one
    

    还有其他更好的解决方案吗?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-06-20
      • 2011-03-20
      • 1970-01-01
      • 2019-06-06
      • 1970-01-01
      • 2019-06-03
      • 2016-06-03
      • 1970-01-01
      相关资源
      最近更新 更多