Scala: как задать границы параметров типа, подразумевающие равенство? - PullRequest
2 голосов
/ 25 октября 2011

Не стоит откладывать на длинный текст, пункты довольно тривиальны, но для иллюстрации проблемы требуется немного кода. : -)

Настройка:

Скажем, я хотел бы создать черту, здесь смоделированную как Converter некоторого вида, которая сама является универсальной, но имеет типизированный метод convert(), который возвращает соответственно типизированный объект результата, скажем Container[T]:

 trait Converter {
   def convert[T]( input: T ) : Container[T]
 } 
 trait Container[T]  // details don't matter

Мой вопрос касается ограничений типов для методов, в частности, для обеспечения равенства, и состоит из двух тесно связанных частей.

Часть 1: Теперь скажите, что существует специальный тип контейнера, который особенно подходит для содержимого на основе массива, например:

 object Container {
   trait ForArrays[U] extends Container[Array[U]] 
 }

Учитывая эту возможность, я бы хотел теперь специализировать конвертер и, в частности, тип возврата метода convert() для специализированного типа Container.ForArrays:

 object Converter {
   trait ForArrays extends Converter {
     // the following line is rubbish - how to do this right?
     def convert[E,T <: Array[E]]( input: T ) : Container.ForArrays[E]
   } 
 }

Так что я могу сделать что-то вроде этого:

val converter = new Converter.ForArrays { ... }
val input = Array( 'A', 'B', 'C' )   
val converted : Container.ForArrays[Char] = converter.convert( input )

По сути, я хочу, чтобы Scala, если известно, что тип преобразователя Converter.ForArrays, также выводил специализированный тип возврата convert[Char]() как Container.ForArrays[Char], то есть соответствующий тип контейнера плюс тип массива входных данных. Возможно ли это или что-то подобное, и если да, то как мне это сделать? Например. Как мне указать параметры типа / границы на convert () (то, что предоставляется, это просто замена - я понятия не имею, как это сделать). Да, и естественно, так что он все еще отменяет свой супер метод, иначе ничего не получится.

Часть 2: В качестве запасного варианта, если бы это было невозможно, я, конечно, мог бы перенести функцию преобразования в вариант, ориентированный на массив, например:

 trait Converter   // now pretty useless as a shared trait
 object Converter {
   trait ForValues extends Converter { 
     def convert[T]( input: T ) : Container[T] 
   }
   trait ForArrays extends Converter {
     def convert[E]( input: Array[E] ) : Container.ForArrays[E]
   } 
 }

OK. Теперь скажите, что у меня есть еще более специализированный Converter.ForArrays.SetBased, который может внутренне использовать набор элементов типа E (такой же, как тип элемента массива 'input'), чтобы совершать определенную магию во время преобразования. Набор теперь является параметром черты, однако так:

 case class SetBased( set: Set[F] ) extends Converter.ForArrays {
   // the following line is also rubbish...
   def convert[E = F]( input: Array[E] ) : Container.ForArrays[E] = {...}
 }

Опять же, речь идет о параметрах типа метода convert (). Сложность заключается в следующем: как мне приклеить параметр типа класса - F - к параметру типа метода - E - так, чтобы компилятор Scala позволял пользователю вызывать convert() только с массивом, элементы соответствуют элементам набора? Пример:

val set = Set( 'X', 'Y', 'Z' )
val converter = new Converter.ForArrays.SetBased( set )
val input = Array( 'A', 'B', 'C' )   
val converted : Container.ForArrays[Char] = converter.convert( input )

Ответы [ 2 ]

4 голосов
/ 25 октября 2011

Нет, вы не можете. По той же причине вы не можете сузить типы аргументов или расширить типы возвращаемых данных при переопределении метода (но можете сузить тип возвращаемых данных). Однако вот что вы можете сделать (для вашего запасного решения):

trait Converter {
  type Constraint[T]
} 

trait ForArrays extends Converter {
  def convert[E]( input: Array[E] )( implicit ev : Constraint[T] ) : Container.ForArrays[E]
}

case class SetBased[F](set: Set[F]) extends Converter {
   type Constraint[T] = T =:= F
   def convert[E]( input: Array[E] )( implicit ev : E =:= F ) = ...
} 
1 голос
/ 25 октября 2011

Я собираюсь предположить, что Container.ForArrays является подклассом Container, без этого Converter.ForArrays.convert не будет соответствовать сигнатуре переопределенного Converter.convert

Попробуйте написать что-то вродеэто:

object Converter {
  trait ForArrays extends Converter {
    def convert[E] (input: Array[E]): Container.ForArrays[E]
  } 
}

Относительно вашего запасного решения.Если два типа одинаковы, просто используйте один и тот же тип param!

case class SetBased (set: Set[F]) extends Converter.ForArrays {
  def convert (input: Array[F]): Container.ForArrays[F] = {...}
}
...