3

Я борюсь с зависимыми типами в Scala 2.11.7. Вот контекст:Заводский метод с зависимым типом

trait Counter { 
    type T 
    def zero: T 
    def incr(t: T): T 
} 
object IntCounter extends Counter { 
    type T = Int 
    val zero = 0 
    def incr(t: Int) = t + 1 
} 

case class Foo(counter: Counter) 

def twice(foo: Foo)(cntr: foo.counter.T): foo.counter.T = 
    foo.counter.incr(foo.counter.incr(cntr)) 

Пока все хорошо, все скомпилировано. Однако я хотел бы добавить объект, который содержит как экземпляр Foo, так и соответствующее состояние счетчика. Например:

trait Bar { 
    val foo: Foo 
    val current: foo.counter.T 
} 

Определение в порядке (при условии, что я использую абстрактные vals). Но я не могу определить фабричный метод (он же умный конструктор). Все мои наивные попытки не скомпилируются. Например, определение:

def bar(f: Foo)(cntr: f.counter.T): Bar = new Bar { 
    val foo = f 
    val current = cntr 
} 

не может скомпилировать с ошибкой:

xxx: overriding value current in trait Bar of type this.foo.counter.T; 
value current has incompatible type 
    val current = cntr 
    ^

Как я могу заставить компилятор понять, что оба типа действительно то же самое? Я мог бы решить проблему с генериками вместо этого, но я предпочитаю избегать этого варианта, если это возможно.

ответ

3

Если bar одиночный конструктор Bar, вы можете решить, как это:

sealed trait Foo { //can't be case class because you can't even call `twice` method then 
    type Ctr <: Counter 
    type Z <: Ctr#T 
    val counter: Ctr 
} 

def foo[Ct <: Counter](ctr: Ct): Foo{type Ctr = Ct} = new Foo { 
    type Ctr = Ct 
    type Z = ctr.T 
    val counter = ctr 
} 


sealed trait Bar { 
    type Ctrr <: Counter 
    type TT <: Counter#T 
    val foo: Foo {type Ctr = Ctrr} 
    val current: TT 
} 

def bar[Ct <: Counter](f: Foo{type Ctr = Ct})(cntr: f.counter.T)(implicit ev: Ct =:= f.Ctr): Bar {type Ctrr = Ct; type TT = f.counter.T} = new Bar { 
    type Ctrr = Ct 
    type TT = f.counter.T 
    val foo = f 
    val current = cntr 
} 

Использование:

scala> val br = bar(foo(IntCounter))(5) 
br: Bar{type Ctrr = IntCounter.type; type TT = Int} = [email protected] 

scala> br.foo.counter.incr(br.current) 
res41: Int = 6 

Недостатком здесь является то, что вы должны определить (и поддерживать) то же самое root-type между TT и foo членов в каких местах вы ввели новый Foo экземпляр.

+0

Мне нравится ваше решение, но, как вы упомянули, оно не выражает того факта, что 'TT' должно быть в то же время, что и' foo.counter.T'. В крайнем случае я мог запечатать черту «Бар». – paradigmatic

+0

К сожалению, это не сработает. Например, 'bar (Foo (IntCounter)) (0)' создает ошибку компиляции. – paradigmatic

+0

@paradigmatic это проблема с оболочкой классов case: 'val c = Foo (IntCounter) .counter; val num: c.T = 5; error: type mismatch' Вероятно, вы даже не можете называть свою функцию 'дважды' – dk14

Смежные вопросы