I have this code that convert an arbitrary size of class to an HList
class Record[H <: HList](val hs: H)
object Record {
import shapeless.LabelledGeneric
def apply[P <: Product, L <: HList](p: P)(implicit gen: LabelledGeneric.Aux[P, L]) = new Record[L](gen.to(p))
}
I would like to use it as follows:
import shapeless._
import syntax.singleton._
val h = Record('a ->> 1, 'b ->> 2)
# h.hs
res91: cmd90.<refinement>.this.type.Out = 1 :: 2 :: HNil
However the keys are not retained after the conversion. As opposed to creating the same HList:
# h.hs.keys //error: could not find implicit value for parameter keys: shapeless.ops.record.Keys[this.Out]
# ('a ->> 1 :: 'b ->> 2 :: HNil).keys //works ok
The question is how to retain the key information in the record.
You use records incorrectly.
Things like 'a ->> 1, 'b ->> 2 shouldn't be used separately, they should be used inside HLists like 'a ->> 1 :: 'b ->> 2 :: HNil.
When you write Record('a ->> 1, 'b ->> 2) the argument of Record is considered as Tuple2. Since Tuple2 is a case class, LabelledGeneric transforms it implicitly into an HList (actually, a shapeless' record). Look at your h, its type is very strange (pseudocode):
Record['_1 -> 'a -> Int :: '_2 -> 'b -> Int :: HNil]
(_1 and _2 are field names of Tuple2).
So how to fix your code depends on what you really wanted to get (I'm not sure). One possible way is
// class Record[H <: HList](val hs: H)
object Record {
def apply[P <: Product, L <: HList](p: P)(implicit gen: LabelledGeneric.Aux[P, L]) = /*new Record[L](*/gen.to(p)/*)*/
}
case class MyCaseClass(a: Int, b: Int)
val h = Record(MyCaseClass(1, 2))
h/*.hs*/ // 1 :: 2 :: HNil
h/*.hs*/.keys // 'a :: 'b :: HNil
('a ->> 1 :: 'b ->> 2 :: HNil).keys // 'a :: 'b :: HNil
I would like to have something like Record('a ->> 1, 'b ->> 2) ... or potentially Record(('a', 1), ('b', 2))
as an input, and an HList 'a ->> 1 :: 'b ->> 2 :: HNil as an output.
Then you don't need LabelledGeneric. You can transform Record('a ->> 1, 'b ->> 2) into just a tuple ('a ->> 1, 'b ->> 2) (it's Record.unapply(...).get if you defined case class Record(p: Product)) and then this tuple into HList:
import shapeless.{::, HNil}
import shapeless.syntax.std.tuple._
import shapeless.syntax.singleton._
('a ->> 1, 'b ->> 2).productElements == 'a ->> 1 :: 'b ->> 2 :: HNil // true
Related
So at runtime I get an Hlist of Hlists which looks like:
(2 :: HNil) :: (1001 :: HNil) :: (1001 :: HNil) :: HNil
Here the type of the resultant Hlist is :
(Int :: HNil) :: (Long :: HNil) :: (Long :: HNil) :: HNil
but this could have been any other type at runtime. For example, it could have been
(Int :: HNil) :: (String :: HNil) :: HNil
Is there a way to flatten this HList like so:
2 :: 1001 :: 1001 :: HNil
with the following type:
Int :: Long :: Long :: HNil
You can do it applying functional operations to HList. Take a look to how Poly works. First, we declare the Poly for (Int::HNil) and (Long::HNil):
import shapeless._
object myPoly extends Poly1 {
implicit val tupleIntCase: Case.Aux[(Int :: HNil), Int] =
at(position => position.head)
implicit val tupleLongCase: Case.Aux[(Long :: HNil), Long] =
at(position => position.head)
}
and using the map function you can extract and flatten the HList:
((2 :: HNil) :: (1001L :: HNil) :: (1001 :: HNil) :: HNil).map(myPoly)
It gives the desired result:
2 :: 1001 :: 1001 :: HNil
Note that the resulting type here is:
Int :: Long :: Int :: HNil
You can use the identity Poly defined by shapeless for conciseness:
import shapeless._
import shapeless.poly.identity
val nestedHList = (2 :: HNil) :: (1001 :: HNil) :: (1001 :: HNil) :: HNil
nestedHList.flatMap(identity)
// 2 :: 1001 :: 1001 :: HNil
I'm trying to do some calculations on HList types (to enforce a tensor algebra). I'm failing so far, using 2.13.0-M4.
Here is the challenge:
type XInt = Int with Singleton
def mult[N <: XInt, M <: XInt, P <: XInt, T <: HList]
(a: N :: M :: T, b: M :: P :: T): N :: P :: T= ???
val a = 8.narrow :: 4.narrow :: HNil
val b = 4.narrow :: 5.narrow :: HNil
mult(a, b) // should yield 8 :: 5 :: HNil
Try
def mult[N <: XInt, M <: XInt, P <: XInt, T <: HList](a: N :: M :: T, b: M :: P :: T): N :: P :: T =
(a, b) match { case (n :: _ :: t, _ :: p :: _) => n :: p :: t }
or
def mult[N <: XInt, M <: XInt, P <: XInt, T <: HList](a: N :: M :: T, b: M :: P :: T): N :: P :: T =
a.head :: b.tail.head :: b.tail.tail
or simply
def mult[N <: XInt, M <: XInt, P <: XInt, T <: HList](a: N :: M :: T, b: M :: P :: T): N :: P :: T =
a.head :: b.tail
I'm essentially looking for the opposite of the type class Prepend[A, B].
If I have something like:
type A = String :: Int :: HNil
type B = Boolean :: Double :: HNil
val a: A = "a" :: 1 :: HNil
val b: B = false :: 2.1 :: HNil
scala> val ab = a ++ b
ab: shapeless.::[String,shapeless.::[Int,shapeless.::[Boolean,shapeless.::[Double,shapeless.HNil]]]] = a :: 1 :: false :: 2.1 :: HNil
I have an HList a of type A and an HList b of type B, I can find a prepend: Prepend[A, B] such that I can concatenate them with a ++ b.
But if I have an HList ab of type prepend.Out, how can I extract the original A and B? I can't seem to find a type class that does the job, and perhaps there isn't one. It seems like I would need something like trait Cut[A <: HList, B <: HList, c <: HList] that witnesses that C has been created by pre-pending A to B, though I'm not sure how I would go about generating witnesses.
Very roughly like:
def Cut[A <: HList, B <: HList, C <: HList](c: C)(implicit cut: Cut[A, B, C]): (A, B) = ???
You can do this fairly straightforwardly with Split:
import shapeless._, ops.hlist.{ Length, Prepend, Split }
class UndoPrependHelper[A <: HList, B <: HList, C <: HList, N <: Nat] {
def apply(c: C)(implicit split: Split.Aux[C, N, A, B]): (A, B) = split(c)
}
def undoPrepend[A <: HList, B <: HList](implicit
prepend: Prepend[A, B],
length: Length[A]
) = new UndoPrependHelper[A, B, prepend.Out, length.Out]
And then:
scala> type A = Int :: String :: Symbol :: HNil
defined type alias A
scala> type B = List[Int] :: Option[Double] :: HNil
defined type alias B
scala> type C = Int :: String :: Symbol :: List[Int] :: Option[Double] :: HNil
defined type alias C
scala> val a: A = 1 :: "foo" :: 'bar :: HNil
a: A = 1 :: foo :: 'bar :: HNil
scala> val b: B = List(1, 2, 3) :: Option(0.0) :: HNil
b: B = List(1, 2, 3) :: Some(0.0) :: HNil
scala> val c: C = a ++ b
c: C = 1 :: foo :: 'bar :: List(1, 2, 3) :: Some(0.0) :: HNil
scala> val (newA: A, newB: B) = undoPrepend[A, B].apply(c)
newA: A = 1 :: foo :: 'bar :: HNil
newB: B = List(1, 2, 3) :: Some(0.0) :: HNil
I recently added an "undo" operation for the Remove type class, and it might make sense to have something similar built into Prepend.
Here is a function I wrote for concatenating elements of a List using an accumulator with tail recursion :
val l1 = List(1, 2, 3) //> l1 : List[Int] = List(1, 2, 3)
val l2 = List(1, 2, 3) //> l2 : List[Int] = List(1, 2, 3)
def func(l1: List[Int], l2: List[Int], acc: List[Int]): List[Int] = {
(l1, l2) match {
case (Nil, Nil) => acc.reverse
case (h1 :: t1, h2 :: t2) => {
func(t1, t2, h1 :: h2 :: acc)
}
}
} //> func: (l1: List[Int], l2: List[Int], acc: List[Int])List[Int]
func(l1, l2, List()) //> res0: List[Int] = List(1, 1, 2, 2, 3, 3)
This is my understanding of the call order
func( 1 :: 1 :: () )
func( 2 :: 2 :: 1 :: 1 : () )
func( 3 :: 3 :: 2 :: 2 :: 1 :: 1 : () )
So the call order is the reason why I must call reverse on base call of acc so that the List is ordered in same ordering initial List elements. To try to minimize the steps required to concatenate the lists I have tried to add the elements like this :
func(t1, t2, acc :: h1 :: h2)
instead of
func(t1, t2, h1 :: h2 :: acc)
but receive compile time error :
value :: is not a member of Int
So it seems I cannot prepend these elements to this List ?
When you write x :: y, y must be a list and x the element you want to prepend.
You can use acc :+ h1 :+ h2 to append h1 and h2 to acc, but note that adding elements to the end of the list is a relatively expensive operation (linear with the length of the list).
How can I find the n! permutations of certain letters on Scala?
Scala 2.9 RC1:
scala> "abc".permutations.toList
res58: List[String] = List(abc, acb, bac, bca, cab, cba)
scala> def permutations[T](xs: List[T]): List[List[T]] = xs match {
| case Nil => List(Nil)
| case _ => for(x <- xs;ys <- permutations(xs diff List(x))) yield x::ys
| }
permutations: [T](xs: List[T])List[List[T]]
scala> permutations("abc".toList) foreach println
List(a, b, c)
List(a, c, b)
List(b, a, c)
List(b, c, a)
List(c, a, b)
List(c, b, a)