Ответ 1
Мне немного легче думать об этой проблеме индуктивно (на уровне типа, по крайней мере). Сначала мы можем определить класс вспомогательного типа, который возвращает N
, если N
является кратным одному из чисел в M
и _0
иначе:
import shapeless._, nat._0, ops.nat.Mod
trait IfMultiple[N <: Nat, M <: HList] { type Out <: Nat }
trait LowPriorityIfMultiple {
type Aux[N <: Nat, M <: HList, Out0 <: Nat] = IfMultiple[N, M] {
type Out = Out0
}
implicit def isMultiple1[N <: Nat, H <: Nat, T <: HList](implicit
ifMultiple: IfMultiple[N, T]
): Aux[N, H :: T, ifMultiple.Out] = new IfMultiple[N, H :: T] {
type Out = ifMultiple.Out
}
}
object IfMultiple extends LowPriorityIfMultiple {
implicit def ifMultiple0[N <: Nat]: Aux[N, HNil, _0] =
new IfMultiple[N, HNil] {
type Out = _0
}
implicit def ifMultiple2[N <: Nat, H <: Nat, T <: HList](implicit
mod: Mod.Aux[N, H, _0]
): Aux[N, H :: T, N] = new IfMultiple[N, H :: T] {
type Out = N
}
}
И теперь нам просто нужен класс типа для добавления всех этих значений от _0
до N - _1
:
import nat._1, ops.nat.Sum
trait SumOfMultiples[N <: Nat, M <: HList] extends DepFn0 { type Out <: Nat }
object SumOfMultiples {
type Aux[N <: Nat, M <: HList, Out0 <: Nat] = SumOfMultiples[N, M] {
type Out = Out0
}
def apply[N <: Nat, M <: HList](implicit
som: SumOfMultiples[N, M]
): Aux[N, M, som.Out] = som
implicit def sum0[M <: HList]: Aux[_1, M, _0] =
new SumOfMultiples[_1, M] {
type Out = _0
def apply(): _0 = _0
}
implicit def sumN[P <: Nat, M <: HList, NV <: Nat, PT <: Nat, NT <: Nat](implicit
ifMultiple: IfMultiple.Aux[P, M, NV],
som: Aux[P, M, PT],
sum: Sum.Aux[NV, PT, NT],
wit: Witness.Aux[NT]
): Aux[Succ[P], M, NT] = new SumOfMultiples[Succ[P], M] {
type Out = NT
def apply(): NT = wit.value
}
}
И затем мы закончим:
import nat._, test.typed
val result = SumOfMultiples[_10, _3 :: _5 :: HNil]
typed[Succ[_22]](result())
Что скомпилируется, как ожидалось.
Стоит отметить, что есть другие способы решения этой проблемы. Вы можете создать класс типа, который обеспечивал бы диапазоны Nat
, а затем сбрасывался с помощью Poly2
с помощью IfMultiple
. Вы также можете определить класс типа IsMultiple
, который только что свидетельствует о том, что N
является кратным одному из чисел в M
. Первая попытка быстро сделала это, но я столкнулся с проблемами двусмысленности, поэтому я пошел с аналогичным версия выше. Однако реализация здесь довольно проста, и если у вас нет других приложений, например. Nat
, я думаю, что это довольно разумное решение.