Определение общих нижних границ параметров

#scala #generics #types #type-bounds

#scala #общие #типы #тип-границы

Вопрос:

В следующей функции компилятор Scala может определять возвращаемый тип с наименьшим общим супертипом значений, используемых в выражении if / else:

 def cond(b: Boolean, t: A, f: B) = if (b) t else f
  

Учитывая следующую иерархию:

 class X
class A extends X
class B extends X
  

приведенная cond выше функция определяется как возвращающая значение типа X .

Однако, если A и B являются параметрами типа в определении cond функции, ее возвращаемый тип Any :

 def cond[A, B](b: Boolean, t: A, f: B) = if (b) t else f
  

Возможно ли заставить компилятор использовать наименьший общий супертип параметров типа?

Я попробовал несколько вариантов следующего, но безуспешно:

 def cond[A, B, R >: A with B](b: Boolean, t: A, f: B): R = if (b) t else f
def cond[A, B, R](b: Boolean, t: A, f: B)(implicit ev1: A <:< R, ev2: B <:< R): R = if (b) t else f
  

РЕДАКТИРОВАТЬ: приведенный выше вопрос упрощен. На самом деле у моей реальной проблемы уже был решен один из параметров типа:

 class C[A](b: Boolean, t: A) {
  def cond[B](f: B) = if(b) t else f
}
  

Ответ №1:

Если вам не нужны точные типы аргументов, обычно достаточно следующего:

 def cond[T](b: Boolean, t: T, f: T) = if (b) t else f
  

Scala автоматически приведет типы аргументов к их наименьшей верхней границе (LUB):

 scala> cond(true, new A, new B)
res0: X = A@74a59bb6
  

Но если вам нужны точные типы, например, для неявного разрешения, я считаю, что следующий трюк должен сработать:

 def cond[A, B, C >: A](b: Boolean, t: A, f: B with C): C = if (b) t else f
  

Здесь A и B указаны точные типы аргументов, а C также их LUB. C ограничение должно быть супертипом A : C >: A , но оно также должно быть типом второго аргумента, потому что оно определено как f: B with C , и, следовательно, оно выводится как LUB из A and B .

Мы можем проверить правильность вывода типа с помощью этого определения с помощью следующего кода:

 import reflect.runtime.universe._
def cond[A, B, C >: A](b: Boolean, t: A, f: B with C)(
  implicit ta: TypeTag[A], 
           tb: TypeTag[B], 
           tc: TypeTag[C]
): C = {
  println(ta)
  println(tb)
  println(tc)
  if (b) t else f
}

scala> cond(true, new A, new B)
TypeTag[A]
TypeTag[B]
TypeTag[X]
res5: X = A@f0ad2ea
  

Ответ №2:

Вы можете применить верхние границы типов к A и B, если вы знаете LUB статически.

 def cond[A <: C, B <: C,C](b: Boolean, t: A, f: B) = if (b) t else f
  

Ответ №3:

Благодаря ответам @nikhil и @Kolmar я смог прийти к этому решению:

 class C[A](b: Boolean, t: A) {
  def cond[B <: R, R >: A](f: B): R = if(b) t else f
}