Взяв HList из Seq[_] и сгенерировав Seq[HList] с декартовым произведением значений
Я новичок в Shapless.
Я пытаюсь написать функцию, которая бы HList
последовательности различных типов, преобразовать его в Seq[HList]
содержащий декартово произведение оригинала HList
и перебрать полученную последовательность
Например:
val input = Seq(true, false) :: Seq(1,2,3)::Seq("foo", "bar") :: HNil
cartesianProduct: Seq[Boolean :: Int :: String :: HNil] = Seq(
true :: 1 :: foo :: HNil,
true :: 1 :: bar :: HNil,
true :: 2 :: foo :: HNil,
true :: 2 :: bar :: HNil,
true :: 3 :: foo :: HNil,
true :: 3 :: bar :: HNil,
false :: 1 :: foo :: HNil,
false :: 1 :: bar :: HNil,
false :: 2 :: foo :: HNil,
false :: 2 :: bar :: HNil,
false :: 3 :: foo :: HNil,
false :: 3 :: bar :: HNil)
Я смог добиться этого в Intellij Scala Worksheet, используя следующий код:
import shapeless._
import shapeless.ops.hlist.LeftFolder
object combine extends Poly {
implicit def `case`[T <: HList, S] = use((acc : Seq[T], curr : Seq[S]) => {
for {
el <- curr
v <- acc
} yield el :: v
})
}
val input = Seq(true, false) :: Seq(1,2,3)::Seq("foo", "bar") :: HNil
val combinations = input.foldLeft(Seq[HNil](HNil))(combine)
combinations.foreach(println)
Здесь все работает, я полагаю, потому что полный тип input
известен компилятору.
Однако, когда я пытаюсь обернуть всю операцию в функцию, полный тип input
теряется, и я не могу позвонить foreach на результат foldLeft
:
def cartesian[T <: HList](input: T)
(implicit folder: LeftFolder[T, Seq[HNil], combine.type]) = {
input.foldLeft(Seq[HNil](HNil))(combine)
.foreach(println)
}
Компилятор жалуется:
value foreach is not a member of folder.Out
input.foldLeft(Seq[HNil](HNil))(combine).foreach(println)
^
Я полагаю, что есть неявное доказательство, которое я могу попросить подтвердить правильную форму input
(HList
из Seq[_]
) и таким образом получить компилятор, чтобы выяснить результирующий тип foldLeft
, но я не могу понять, что это может быть...
Надеюсь, кто-то может помочь мне разобраться в этом. Благодарю.
Обновление: моя конечная цель с этим вопросом была, учитывая HList
из Seq[_]
получить функцию (возможно, для класса case), которая будет принимать функцию с той же аргументностью, что и входной HList, и типы аргументов, соответствующие типам элементов 'Seq' в том же порядке. Например, для ввода выше функция будет f: (Boolean, Int, String) => R
Таким образом, я смог бы перебрать декартово произведение входных данных, используя f
, Окончательный код выглядит так:
import shapeless._
import shapeless.ops.function.FnToProduct
import shapeless.ops.hlist.LeftFolder
import shapeless.syntax.std.function.fnHListOps
object combine extends Poly {
implicit def `case`[EL <: HList, S] = use((acc : Seq[EL], curr : Seq[S]) => {
for {
el <- curr
v <- acc
} yield el :: v
})
}
case class Cartesian[R <: HList, F, FR](combinations: Seq[R])
(implicit ftp: FnToProduct.Aux[F, R => Unit]) {
def foreach(f: F) = combinations.foreach(f.toProduct)
}
def cartesian[T <: HList, R <: HList, F, FR](variants: T)(implicit
folder: LeftFolder.Aux[T, Seq[HNil], combine.type, _ <: Seq[R]],
fnToProd: FnToProduct.Aux[F, R => Unit]
) = {
val combinations: Seq[R] = variants.foldLeft(Seq[HNil](HNil))(combine)
Cartesian(combinations)
}
val variants = Seq(true, false) :: Seq("foo", "bar") :: Seq(1, 2, 3) :: HNil
cartesian(variants).foreach((a, b, c) => println(s"$a, $b, $c"))
Обратите внимание, что типы для аргументов функции a
, b
, c
правильно выведены и Boolean
, String
, а также Int
, В настоящее время тип результата функции передан в foreach
должно быть исправлено (в приведенном выше коде это Unit
). Это не может быть выведено из переданной функции.
1 ответ
Проблема не в том, что компилятор ничего не знает о вводе, а в том, что он ничего не знает о выводе.
внутри def cartesian
все, что известно компилятору, это то, что после foldLeft
вы получаете какой-то тип folder.Out
(что зависит от того, что фигура будет бесформенной для вас).
Для обеспечения типов результатов, вы можете использовать LeftFolder.Aux
с одним дополнительным параметром типа, например
def cartesian[T <: HList](input: T)
(implicit folder: LeftFolder.Aux[T, Seq[HNil], combine.type, _ <: Seq[Any]]) = {
input.foldLeft(Seq[HNil](HNil))(combine)
.foreach(println)
}
Теперь компилятор будет знать, что результатом является некоторый подтип Seq[Any]
так можно позвонить foreach
в теме.
Конечно, это только проблема внутри def
, При вызове сайтов типы вывода будут решаться на основе ввода, так что вы сможете сделать это без Aux
:
def cartesian2[T <: HList](input: T)
(implicit folder: LeftFolder[T, Seq[HNil], combine.type]) = {
input.foldLeft(Seq[HNil](HNil))(combine)
}
cartesian2(input).foreach(println)
Запускаемый код: https://scalafiddle.io/sf/n409yNW/2