#scala #tuples #dotty #scala-3
#scala #Кортежи #dotty #scala-3
Вопрос:
По независящим от меня причинам мой метод получает входные данные в виде кортежа. Этот кортеж должен содержать только экземпляры Foo
, т. Е. Он должен выглядеть как (Foo, Foo ... Foo)
и не должен иметь String
или Int
внутри. Я хочу проверить это во время компиляции, а не создавать исключение во время выполнения. Как я могу этого добиться?
Ниже приведен код, который у меня есть в настоящее время, что неверно:
def f(tupleOfFoos: Tuple): Tuple = {
for (x <- tupleOfFoos) assert(x.isInstanceOf[Foo])
mapTuple(tupleOfFoos, irrelevantFunction)
}
Я открыт для использования Shapeless или новых функций, представленных в Dotty / Scala 3.
Ответ №1:
В Scala 2 с помощью Shapeless вы можете сделать это (Scastie):
def f[T <: Product, H <: HList](tupleOfFoos: T)(
implicit gen: Generic.Aux[T, H],
hev: LiftAll[({type E[T] = Foo =:= T})#E, H]
) = tupleOfFoos
LiftAll
гарантирует Foo =:= X
, что для каждого X
in есть экземпляр H
, и gen
гарантирует, что T
и H
не являются полностью несвязанными типами.
В Dotty вы можете добавить параметр доказательства с типом соответствия для этого:
type Homogenous[H, T <: Tuple] = T match {
case EmptyTuple => DummyImplicit
case H *: t => Homogenous[H, t]
case _ => Nothing
}
def f[T <: Tuple](tupleOfFoos: T)(using Homogenous[Foo, T]) = tupleOfFoos
Это позволит вам вызывать f((Foo(), Foo(), Foo()))
, но нет f((1, 2, 3))
.
Homogenous
это рекурсивный тип соответствия с базовым случаем EmptyTuple
. Если кортеж пуст, то он не заполняется не- Foo
s , поэтому тип становится DummyImplicit
, который уже имеет неявное значение в области видимости. В противном случае мы проверяем, выглядит ли он как (H, ...)
/ H *: t
, и в этом случае нам нужно проверить, действительна ли остальная часть кортежа ( t
) . Если это не соответствует второму случаю, мы знаем, что кортеж недействителен, и в этом случае результатом будет Nothing
то, что здравомыслящие люди не делают неявных значений.
Если вы хотите использовать границы контекста, вы можете создать дополнительный тип curried (Scastie):
type Homo2[H] = [T <: Tuple] =>> Homogenous[H, T]
def f[T <: Tuple : Homo2[Foo]](tupleOfFoos: T) = tupleOfFoos
К сожалению, я не смог заставить его работать с одним типом curried (Scastie):
type Homogenous[H] = [T <: Tuple] =>> T match {
case EmptyTuple => DummyImplicit
case H *: t => Homogenous[H][t]
case _ => Nothing
}