Scala的路径依赖型内线等级值(Path-Dependent type inside class

2019-08-31 23:45发布

我想给一个类型的值与抽象型的一类,并在以后使用它的路径依赖型。 请看下面的例子(使用的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.aA =:= foo.A )。 这是Scala编译器的限制?

其次,我怎么能做到什么,我试图做的?

UPDATE

看来,这可以通过使用泛型和内嵌式的约束来实现:

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

然而,在我的情况下, A实际上是一个高kinded型,所以例如变为:

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[_]

}

Answer 1:

在你原来的问题,你的问题是,Scala编译器是无法证明的结果类型的平等foo.makeA与参数类型的fooWrap.a.useA 。 要做到这一点就需要能够证明身份foofooWrap.a ,我们可以直观地看到必须是这里的情况,但它不是简单的编译器进行跟踪。

有一对夫妇的方法来解决这个问题。 首先,你可以使用fooWrap.a均匀代替foo

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

现在,它的简单编译器来识别(前缀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>


Answer 2:

较高kinded型可以被添加到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

任何其他更好的解决方案?



文章来源: Path-Dependent type inside class value in Scala