我想将一个带有抽象类型的类型的值赋给类,然后使用它的路径依赖类型。请看以下示例(使用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[_]
}
答案 0 :(得分:3)
在您的原始问题中,您的问题是Scala编译器无法证明foo.makeA
的结果类型与参数类型fooWrap.a.useA
相等。要做到这一点,它需要能够用foo
来证明fooWrap.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>
答案 1 :(得分:0)
较高的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
还有其他更好的解决方案吗?