2013-04-26 1 views
3

Вкратце: следующее не компилируется (причина ниже), как я могу заставить его работать?Scala: повторное использование родового в результате зависимого от пути типа в зависимом от пути контексте

trait Simulator { 
    type CM[T] 
    def useCM(v: CM[_]) 
} 

case class CMH[S <: Simulator,T](cm: S#CM[T]) 

class SimIterator[S <: Simulator](val sim: S, val cmhs: Seq[CMH[S,_]]) { 
    cmhs foreach { cmh => sim.useCM(cmh.cm) } 
    /* 
    compile time error: 
    type mismatch; found : cmh.cm.type (with underlying type S#CM[_$2]) required: 
    SimIterator.this.sim.CM[_] Note: _$2 <: Any (and cmh.cm.type <: S#CM[_$2]), 
    but type CM is invariant in type T. You may wish to define T as +T instead. 
    (SLS 4.5) 
    */ 
} 

Идея структуры является то, что CMH скрывает T Определённого поведения от SimIterator, где, как последние обрабатывают общие задачи. S используется для принудительного vlaues в CMH, чтобы иметь нужный тип без экземпляра Simulator.

В foreach, похоже, проблема с подтипированием связана с CM. Если S#CM является конкретным типом, нам необходимо sim.CM =:= S#CM. Однако, обратите внимание на следующее:

object Test extends Simulator { 
    type CM[T] = Option[T] 
    def useCM(v: CM[_]) = println(v) 
    def mkCM[T]: CM[T] = None 

    CMH[Simulator,AnyRef](mkCM[AnyRef]) 
} 

Мы имеем теперь CMH, что мы можем перейти в SimIterator вместе с любым Simulator. По-видимому, типизация SimIterator не является достаточно ограничительной. Как можно выразить (и использовать) S =:= sim.type?

ОБНОВЛЕНИЕ

Это работает, но не может быть использовано в конструкторе (незаконный зависимый типа метода: параметр появляется в типе другого параметра в той же самой секции или раньше)

class SimIterator(val sim: Simulator) { 
    def doIt(cmhs: Seq[CMH[sim.type,_]]) { 
    cmhs foreach { cmh => sim.useCM(cmh.cm) } 
    } 
} 

Верхний пример работает, но не что я хочу. cmhs должен быть передан при строительстве.

ответ

2

Вы можете легко решить проблему, переместив абстрактный элемент типа в качестве параметра типа, как это:

trait Simulator[CM[_]] { 
    def useCM(v: CM[_]) 
} 

case class CMH[CM[_]](cm: CM[_]) 

class SimIterator[S <: Simulator[CM], CM[_]](val sim: S, val cmhs: Seq[CMH[CM]]) { 
    cmhs foreach { cmh => sim.useCM(cmh.cm) } 
} 

Таким образом, можно избежать с помощью типа, если вам действительно нужно сохранить CM как член типа, пожалуйста, предоставьте более подробное объяснение, в каком случае вам нужна проекция типа и почему.

EDIT/UPDATE

Добро пожаловать в "Хлебное Гибели"!;-)

Существует два решения в этом случае:

первый, поставить вас итератор в торт, так что вы можете получить доступ к типу непосредственно:

case class CMH[CM[_]](cm: CM[_]) 

trait Cake { 
    type CM[_] 

    trait Simulator { 
    def useCM(v: CM[_]) 
    } 

    class SimIterator[S <: Simulator](val sim: S, val cmhs: Seq[CMH[CM]]) { 
    cmhs foreach { cmh => sim.useCM(cmh.cm) } 
    } 
} 

или второй, encapuslate ваш итератор в другом классе, что делает возможным доступ к пути в зависимости от типа:

trait Cake { 
    type CM[_] 

    trait Simulator { 
    def useCM(v: CM[_]) 
    } 
} 

case class CMH[CM[_]](cm: CM[_]) 

class SimIteratorBuilder[C <: Cake](val cake: Cake) { 
    class SimIterator(val sim: cake.Simulator, val cmhs: Seq[CMH[cake.CM]]) { 
    cmhs foreach { cmh => sim.useCM(cmh.cm) } 
    } 
} 

Как только вы окажетесь в пироге, нет способа избежать этого!

+0

Благодарим вас за ответ. «Симулятор» используется в шаблоне пирога (для испечения конкретного симулятора с разными моделями), откуда и возникает член типа. 'SimIterator' находится за пределами области торта и может использовать некоторые конкретные реализации' Simulator' (ограничения типов, не показанные в этом примере) для создания итерационных симуляций при сохранении данных 'CM' (типа' T') между прогонами , Добавление параметра типа в «Симулятор» не ухудшается с точки зрения «SimIterators», но полностью нарушает абстракцию типа образца пирога. 'cmhs' является гетерогенным в' T', следовательно, проекция. – gzm0

+0

@ gzm0 Хорошо, справедливо. Это имеет смысл. Я отредактировал свой ответ с возможным решением. Удачи вам в торте :-) Я лично стараюсь избегать этой картины как можно больше, чтобы избежать этой пекарни обреченности. –

+0

Большое спасибо, это имеет смысл. Но разве это не просто синтаксическое ограничение? Во вторичном конструкторе из моего ответа вполне можно заставить тип, как я хочу. Есть ли причина, по которой это невозможно распространить на основной конструктор? – gzm0

0

ПОЖАЛУЙСТА Кто-то придумал лучшее решение. (Да, эквивалент useEm также может быть выполнен в публичном конструкторе, но это позволяет вызвать его снова).

class SimIterator[S <: Simulator] private (
    val sim: Simulator, 
    val useEm:() => Unit) { 

    def this(sim: Simulator)(cmhs: Seq[CMH[sim.type,_]]) = { 
    this(sim,() => cmhs foreach { cmh => sim.useCM(cmh.cm) }) 
    } 

    useEm() 

} 

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

object Test extends Simulator { 
    type CM[T] = Option[T] 
    def useCM(v: CM[_]) = println(v) 
    def mkCM[T]: CM[T] = None 

    val cm = CMH(mkCM[AnyRef]) 

    new SimIterator(this)(cm :: Nil) 
}