#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
}