Splitting an HList that was concatenated using Prepend[A, B] - scala

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.

Related

How to write curried polymorphic function & its higher kind in Scala 3?

In Scala 3, I'm able to write a poly-function of type 1:
val y = [C <: Int] => (x: C) => x * 2
When I try to generalise it into type 2:
val z = [C <: Int] => ([D <: Int] => (x: C, y: D) = x * y)
I got the following error:
DependentPoly.scala:19:37: Implementation restriction: polymorphic function literals must have a value parameter
So is this feature not implemented? Or I'm not writing it properly?
Implementation restriction: polymorphic function literals must have a value parameter means that
val y = [C <: Int] => foo[C]
is illegal (for example for def foo[C <: Int]: C => Int = _ * 2) while
val y = [C <: Int] => (x: C) => x * 2
is legal.
Similarly,
val z = [C <: Int] => [D <: Int] => (x: C, y: D) => x * y
val z = [C <: Int] => [D <: Int] => (x: C) => (y: D) => x * y
are illegal while
val z = [C <: Int, D <: Int] => (x: C, y: D) => x * y
val z = [C <: Int, D <: Int] => (x: C) => (y: D) => x * y
val z = [C <: Int] => (x: C) => [D <: Int] => (y: D) => x * y
val z = [C <: Int] => (_: C) => [D <: Int] => (x: C, y: D) => x * y
val z = [C <: Int] => (_: C) => [D <: Int] => (x: C) => (y: D) => x * y
are legal.
This is because of
trait PolyFunction:
def apply[A](x: A): B[A]
https://docs.scala-lang.org/scala3/reference/new-types/polymorphic-function-types.html
https://github.com/lampepfl/dotty/pull/4672

How to flatten an HLists of HLists

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

How to implement HList literal type constraints?

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

retain key information in implicit LabelledGeneric

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

Scala permutation of Factorials

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)