2013-04-23 1 views
3

Я хотел бы указать значение типа с абстрактным типом для класса, а затем использовать его зависимый от пути тип. Посмотрите на следующий пример (с помощью Scala 2.10.1):Path-Dependent type внутри значения класса в 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

ответ

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

Любых другие, более хорошие решения?