9

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)  = ???
Michael Zajac
  • 55,144
  • 7
  • 113
  • 138
  • you may consider [tagging](http://eed3si9n.com/learning-scalaz/Tagged+type.html) (warning: scalaz tagged types aren't so cool as they was) of every element with some unique type (generate it as path-dependent), like `val a: A = "a" :: 1 :: HNil tagged; val a: B = "a" :: 1 :: HNil tagged` and then just filter every list by type. just idea - didn't check :) – dk14 Sep 03 '15 at 08:34

1 Answers1

6

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.

Travis Brown
  • 138,631
  • 12
  • 375
  • 680