Scala 中类值中的路径相关类型 [英] Path-Dependent type inside class value in Scala

查看:38
本文介绍了Scala 中类值中的路径相关类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I would like to give a value of a type with an abstract type to a class and later use it's path dependent type. Look at the following example (using 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 编译器的限制吗?

First, I do not understand the fundamental difference between the local and the class-value case (note the public, immutable value) and why the type checking fails (because obviously Test.fooWrap.a.A =:= foo.A). Is this a limitation of the Scala compiler?

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

Second, how can I achieve what I am trying to do?

更新

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

It seems that this can be achieved by using generics and inline type-constraints:

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

然而,在我的例子中,A 实际上是一个更高级的类型,所以这个例子变成了:

However, in my case, A is actually a higher-kinded type, so the example becomes:

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 编译器无法证明 foo.makeA 的结果类型与fooWrap.a.useA 的参数类型.要做到这一点,它需要能够用 fooWrap.a 证明 foo 的身份,我们可以直观地看到这里一定是这种情况,但这并不简单供编译器跟踪.

In your original question, your problem is that the Scala compiler is unable to prove equality of the result type of foo.makeA with the argument type of fooWrap.a.useA. To do that it would need to be able to prove the identity of foo with fooWrap.a which we can intuitively see must be the case here, but which isn't straightforward for the compiler to track.

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

There are a couple of ways to work around this problem. First, you could use fooWrap.a uniformly in place of foo,

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

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

Now it's simple for the compiler to recognize the prefix of A (fooWrap.a) as being the same in both occurrences.

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

Second, you could parametrize FooWrap in a way which captures the type of its Foo argument more precisely,

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 的结果类型中.

Here the type argument of FooWrap is inferred as IntFoo rather than as bare Foo, hence A is known to be exactly Int, as it is in the result type of foo.makeA.

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

In your update you introduce an additional wrinkle: you change the signature of useA to,

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

这里的 _ 是存在的,它会挫败所有哄骗编译器证明有用的类型相等性的尝试.相反,你需要一些类似的东西,

The _ here is an existential which will frustrate all attempts to coax the compiler into proving useful type equalities. Instead you need something along the lines of,

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 会话,

Sample REPL session,

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

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

scala>

这篇关于Scala 中类值中的路径相关类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆