не удалось найти неявное значение для кортежа параметра

В приведенном ниже коде я пытаюсь создать HList of Lists, но я столкнулся с ошибкой времени компиляции в последней строке этого кода:

◾ не удалось найти неявное значение для параметра кортеж: shapeless.ops.hlist.Tupler [shapeless.HList] ◾ недостаточно аргументов для метода tupled: (неявный кортеж: shapeless.ops.hlist.Tupler [shapeless.HList]) tupler.Out . Неопределенный кортеж параметра значения.

object Problem extends App {

  def combinations[T](n: Int, ls: List[T]) = {

    import shapeless._
    import HList._

    def prepareHListR(t: Int, result: HList): HList = t match {
      case t if (t == 0) => result
      case _             => prepareHListR(t - 1, rotateLeft(t - 1, ls) :: result)
    }

    prepareHListR(n, HNil)

  }

  def rotateLeft[A](i: Int, xs: List[A]) = {
    val rot = if (i > 0) i else xs.length + i
    xs.drop(rot) ++ xs.take(rot)
  }

  println(combinations(3, List('a, 'b, 'c, 'd, 'e, 'f)))

}

Вывод:

List ('a,' b, 'c,' d, 'e,' f) :: List ('b,' c, 'd,' e, 'f,' a) :: List ('c,' d, 'e,' f, 'a,' b) :: HNil


Что дальше мне нужно сделать, так это создать кортеж из этих списков, как показано ниже:

(Список ('a,' b, 'c,' d, 'e,' f), List ('b,' c, 'd,' e, 'f,' a), List ('c,' d , 'e,' f, 'a,' b))

Для чего я пытаюсь:

combinations(3, List('a, 'b, 'c, 'd, 'e, 'f)).tupled


Однако этот подход отлично работает с REPL:

scala> import shapeless._
import shapeless._

scala> import HList._
import HList._

scala> val hlist = List(1, 2, 3) :: List(4, 5, 6) :: List(7, 8, 9) :: HNil
hlist: shapeless.::[List[Int],shapeless.::[List[Int],shapeless.::[List[Int],shapeless.HNil]]] = List(1, 2, 3) :: List(4, 5, 6) :: List(7, 8, 9) :: HNil

scala> val t  =hlist.tupled
t: (List[Int], List[Int], List[Int]) = (List(1, 2, 3),List(4, 5, 6),List(7, 8, 9))


Мне это кажется проблемой с параметром типа, но я не могу понять это из-за моих ограниченных знаний как в Scala, так и в Shapeless.

Любая помощь высоко ценится! TIA.


person iamsmkr    schedule 07.11.2016    source источник
comment
Правильно, вам, вероятно, не хватает параметра типа (и части неявного доказательства). Если бы вы могли предоставить полный пример (с импортом и всеми определениями), было бы легче диагностировать.   -  person Travis Brown    schedule 07.11.2016
comment
@TravisBrown Спасибо за ответ. Я обновил сообщение рабочим кодом и дальнейшими пояснениями.   -  person iamsmkr    schedule 07.11.2016


Ответы (1)


Здесь есть пара связанных проблем, но важный момент заключается в том, что размер кортежа зависит от значения, которое известно только во время выполнения (что является проблемой). Версия HList работает, но только потому, что вы выбрасываете типы. В итоге вы получаете что-то, статически типизированное как HList, с которым вы практически ничего не можете сделать (кроме сопоставления с образцом во время выполнения), и что обычно не то, что вам нужно.

Вы можете написать такую ​​версию, которая даст вам хороший статически типизированный кортеж, но это немного больше работы и налагает ограничение на ваш n аргумент. Сначала вам нужен класс нестандартного типа:

import shapeless._, ops.tuple.{ Last, Prepend }

trait Rotated[N <: Nat, A] extends DepFn1[List[A]]

object Rotated {
  type Aux[N <: Nat, A, Out0] = Rotated[N, A] { type Out = Out0 }

  implicit def rotated0[A]: Aux[nat._1, A, Tuple1[List[A]]] =
    new Rotated[nat._1, A] {
      type Out = Tuple1[List[A]]

      def apply(l: List[A]): Tuple1[List[A]] = Tuple1(l)
    }

  implicit def rotatedN[N <: Nat, A, OutN, LA](implicit
    rotN: Aux[N, A, OutN],
    last: Last.Aux[OutN, LA],
    ev: LA <:< List[A],
    prep: Prepend[OutN, Tuple1[List[A]]]
  ): Aux[Succ[N], A, prep.Out] =
    new Rotated[Succ[N], A] {
      type Out = prep.Out

      def apply(l: List[A]): prep.Out = {
        val resultN = rotN(l)
        val lastList = ev(last(resultN))
        prep(resultN, Tuple1(lastList.drop(1) ++ lastList.take(1)))
      }
    }
}

Есть несколько способов написать это. Версия выше нестандартная, но, вероятно, разумная.

Теперь ваш combinations метод выглядит так:

def combinations[T, O <: HList](n: Nat, ls: List[T])(implicit
  rotN: Rotated[n.N, T]
): rotN.Out = rotN(ls)

И тогда использование - это именно то, о чем вы просили:

scala> combinations(3, List('a, 'b, 'c, 'd, 'e, 'f))
res0: (List[Symbol], List[Symbol], List[Symbol]) = (List('a, 'b, 'c, 'd, 'e, 'f),List('b, 'c, 'd, 'e, 'f, 'a),List('c, 'd, 'e, 'f, 'a, 'b))

scala> combinations(2, List('a, 'b, 'c, 'd, 'e, 'f))
res1: (List[Symbol], List[Symbol]) = (List('a, 'b, 'c, 'd, 'e, 'f),List('b, 'c, 'd, 'e, 'f, 'a))

Теперь вы получаете обратно кортежи за счет невозможности сделать что-то вроде этого:

scala> val x = 4
x: Int = 4

scala> combinations(x, List('a, 'b, 'c, 'd, 'e, 'f))
<console>:20: error: Expression x does not evaluate to a non-negative Int literal
       combinations(x, List('a, 'b, 'c, 'd, 'e, 'f))
                    ^

Но если вы думаете об этом, это разумное ограничение - если вам нужен кортеж, размер которого доступен во время компиляции, вам нужно указать этот размер таким образом, чтобы он был доступен во время компиляции.

person Travis Brown    schedule 07.11.2016