1
votes

In the code below I am trying to create a HList of Lists but I am facing a compile time error on the last line of this code as:

◾could not find implicit value for parameter tupler: shapeless.ops.hlist.Tupler[shapeless.HList] ◾not enough arguments for method tupled: (implicit tupler: shapeless.ops.hlist.Tupler[shapeless.HList])tupler.Out. Unspecified value parameter tupler.

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)))

}

Output:

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


What next I need to do is to create a tuple of these lists as below:

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

For which I am trying:

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


This approach however is working fine on 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))


To me this seems like an issue with type parameter but I am not able to understand it due to mine limited knowledge in both Scala and Shapeless.

Any help is much appreciated! TIA.

1
Right, you're probably missing a type parameter (and a piece of implicit evidence). If you could provide a complete example (with imports and all definitions) it would be easier to diagnose.Travis Brown
@TravisBrown Thanks for your reply. I have updated the post with the working code and further explanations.iamsmkr

1 Answers

1
votes

There are a couple of related issues here, but the important point is that the size of the tuple depends on a value that's only known at runtime (which is a problem). The HList version works, but only because you're throwing away the types. You're ending up with something that's statically typed as HList, which you can do practically nothing with (except pattern match at runtime), and which generally isn't what you want.

You can write a version of this that gives you a nice, statically tuple-typed result, but it's a little more work, and it puts a constraint on your n argument. First you need a custom type class:

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

There are several ways you could write that. The version above is off-the-cuff but probably reasonable.

Now your combinations method looks like this:

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

And then the usage is exactly what you asked for:

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))

Now you're getting back tuples, at the cost of not being able to do something like this:

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))
                    ^

But if you think about it that's a reasonable constraint—if you want a tuple whose size is available at compile time, you need to specify that size in a way that's available at compile time.