2013-04-23 83 views
3

我想給抽象類型賦予一個類的值給一個類,然後使用它的路徑依賴類型。請看下面的例子(使用斯卡拉2.10.1):Scala中的路徑依賴類內部類值

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編譯器的限制嗎?

其次,我該如何實現我想要做的?

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

} 
+0

http://stackoverflow.com/questions/14544269/scala-immutability-and-path-dependent的可能的複製型兼容性 – gzm0 2013-04-23 22:33:42

回答

3

在你原來的問題中,你的問題是,斯卡拉編譯器無法證明foo.makeA的結果類型與參數類型fooWrap.a.useA相等。要做到這一點,它需要能夠證明foofooWrap.a的身份,我們可以直觀地看到這裏的情況,但編譯器不能直接跟蹤。

有幾種方法可以解決此問題。首先,你可以代替foo使用fooWrap.a均勻,

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

現在是簡單的編譯器來識別(fooWrap.a)的前綴爲在這兩個事件是相同的。

其次,你可以在更精確地捕捉其Foo參數的類型的方式參數化FooWrap

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] = [email protected] 

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] = [email protected] 

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

scala> 
0

較高的親屬類型也可以作爲泛型參數添加到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 

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

相關問題