2012-06-27 6 views
3

У меня есть две черты, каждая из которых имеет параметр типа для одного из своих членов. В первом признаке у меня есть функция, которая принимает экземпляр второго признака и экземпляр элемента второго признака. Эта функция вызывает функцию во втором признаке, ожидающем этого экземпляра члена своего типа. Однако я не могу понять, как правильно параметризовать вызов, чтобы это действительно работало. Вот упрощенный пример того, что не удается:Scala: ограничение типа параметра на основе члена типа другого параметра

trait Garage { 
    type CarType <: Car 
    def Cars: Seq[CarType] 

    def copy(Cars: Seq[CarType]): Garage 

    def Refuel(car: CarType, fuel: CarType#FuelType): Garage = { 
    val car_index = Cars.zipWithIndex.find(_._1 == car).get._2 

    copy(Cars.updated(car_index, car.Refuel(fuel))) 
    } 
} 

trait Car { 
    type FuelType <: Fuel 
    def Fuel: FuelType 

    def copy(Fuel: FuelType): Car 

    def Refuel(fuel: FuelType): Car = { 
    copy(fuel) 
    } 
} 

trait Fuel 

Это терпит неудачу со следующей ошибкой:

error: type mismatch; 
found : fuel.type (with underlying type Garage.this.CarType#FuelType) 
required: car.FuelType 
    copy(Cars.updated(car_index, car.Refuel(fuel))) 
              ^

Как сдерживало функцию Garage.Refuel так, что она принимает Car и любой Fuel, который является приемлемым для что тип Car?

ответ

4

Хотя ответ Дэниела работает, я хотел бы отметить, альтернативу, которая является своего рода моей панацеей. Я много боролся с тем, чтобы правильно определить типы, зависящие от пути, и закончил со следующей стратегией. Это немного больше «некрасиво», как теперь нужно написать дополнительный параметр типа, но этот подход никогда не подводил меня:

trait Garage { 
    type CarType <: Car[CarType] // CarType appears as representation type on the right 
    def cars: Seq[CarType] 

    def copy(Cars: Seq[CarType]): Garage 

    def refuel(car: CarType, fuel: CarType#FuelType): Garage = copy(
    cars.map { // map is more concise for what you try to achieve 
     case `car` => car.refuel(fuel) // backticks to find the particular car 
     case other => other 
    }) 
} 

trait Car[C <: Car[C]] { // add a 'representation type' 
    type FuelType <: Fuel 
    def fuel: FuelType 

    // use 'C' instead of 'Car' everywhere, and qualify the type member with 'C#' 
    def copy(fuel: C#FuelType): C 

    def refuel(fuel: C#FuelType): C = copy(fuel) 
} 

trait Fuel 

Я не знаю, если это понятие «тип представления» имеет официальное название (Мне было бы интересно узнать). Я пытался найти, кто научил меня этому, но не нашел его (по крайней мере, в stackoverflow).

+0

Это действительно работает. Я пытаюсь понять, что это даже делает с чертой. Я по-прежнему удивляюсь проблемам, которые могут быть исправлены в Scala, просто вставляя параметр ограниченного типа перед чем-то, а затем говоря, что вы можете вернуть этот тип. – drhagen

+0

Кроме того, это намного лучший способ заменить данный элемент массива. – drhagen

+4

Когда у вас есть такая рекурсия в ограничении, она называется [_F_-ограниченным полиморфизмом] (http://en.wikipedia.org/wiki/Bounded_quantification). –

7

Try:

def Refuel(car: CarType)(fuel: car.FuelType): Garage = { 
+0

Я боялся этого ответа. Но я полагаю, что это нормально, если мне не нужно делать слишком много вызовов функции; в противном случае дополнительные круглые скобки становятся раздражающими. – drhagen

+0

Я взял слово Sciss за это, что это сработает, но оказывается, что в Scala 2.9.2, во всяком случае, он не компилируется и дает следующие ошибки, которые на самом деле не делают: 'ошибка: незаконная зависимость тип метода; def Refuel (^ автомобиль: CarType) (топливо: автомобиль.FuelType): Garage = {;; ошибка: несоответствие типа; найдено: Seq [this.Car]; требуется: Seq [Garage.this.CarType]; копия (Cars.updated^(car_index, car.Refuel (fuel))); '(стрелки-стрелки, вставленные непосредственно в линию) – drhagen

+0

@drhagen Вам необходимо включить зависимые типы методов с флагом компилятора. –

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