My goal is to Wrap an HList and save enough information to perform prepend and split operations at a later time.
case class Wrap[L <: HList](wrapped: L)
val x = Wrap("x" :: "y" :: HNil)
val y = Wrap(1 :: 2 :: HNil)
case class Append[L1, L2](w1: Wrap[L1], w2: Wrap[L2], prepend: Prepend[L1, L2], length: Length[L1])
def append[L1, L2](w1: Wrap[L1], w2: Wrap[L2])(implicit prepend: Prepend[L1, L2], length: Length[L1]) = Append(w1, w2, prepend, length)
val xAppendY = append(x,y)
val merged = xAppendY.prepend(xAppendY.w1.wrapped, xAppendY.w2.wrapped)
val split = Split[xAppendY.prepend.Out, xAppendY.length.Out] // <-- error here
split.apply(merged)
This code fails with the implicit not found error:
Implicit not found: shapeless.Ops.Split[xAppendY.prepend.Out, xAppendY.length.Out]. You requested to split at position xAppendY.length.Out, but the HList xAppendY.prepend.Out is too short.
But it seems the the compiler should know that the types are String :: String :: String :: String :: HNil and Nat._2. Is there something that I need to do to help the compiler out here?
The following version of code works:
import shapeless.ops.hlist.{Length, Prepend, Split}
import shapeless.{::, HList, HNil, Nat}
case class Wrap[L <: HList](wrapped: L)
val x = Wrap("x" :: "y" :: HNil)
val y = Wrap(1 :: 2 :: HNil)
case class Append[L1 <: HList, L2 <: HList, L3 <: HList, N <: Nat](w1: Wrap[L1], w2: Wrap[L2], prepend: Prepend.Aux[L1, L2, L3], length: Length.Aux[L1, N])
def append[L1 <: HList, L2 <: HList, L3 <: HList, N <: Nat](w1: Wrap[L1], w2: Wrap[L2])(implicit prepend: Prepend.Aux[L1, L2, L3], length: Length.Aux[L1, N]) = Append(w1, w2, prepend, length)
val xAppendY = append(x,y)
val merged = xAppendY.prepend(xAppendY.w1.wrapped, xAppendY.w2.wrapped)
val split = Split[xAppendY.prepend.Out, xAppendY.length.Out]
split.apply(merged)
In your version of code xAppendY.prepend was of type Prepend[L1, L2] = Prepend[L1, L2] { type Out } rather than Prepend.Aux[L1, L2, L3] = Prepend[L1, L2] { type Out = L3 } for proper L3 and xAppendY.length was of type Length[L1] = Length[L1] { type Out } rather than Length.Aux[L1, N] = Length[L1] { type Out = N } for proper N.
Why do we need to specify a refined type (or its equivalent Aux) for the output of certain type computations?
Related
I have a Slick (grouping) query returning a tuple of integers which should be then combined using one of SQL aggregation functions. The size of the tuple is not dynamic but I would like to be able to define it in a single place with possibility to change it later. I use shapeless elsewhere in my project so it is natural to use it here as well.
So, let's say we have a tuple type type Agg = (Rep[Int], Rep[Int], Rep[Int]) and a query with type Query[Agg, _, Seq]
I can then define poly's for aggregation like this:
object sum extends (Query[Rep[Int], Int, Seq] -> Rep[Int])(_.sum.getOrElse(0))
object sumAgg extends(Query[Agg, _, Seq] -> Agg)(q => (q.map(_._1), q.map(_._2), q.map(_._3)).map(sum))
But I can't find a way to get rid of explicit tuple unzipping in sumAgg poly. How can I transform a Query of tuple of Int's (aka Agg) into a tuple of queries of Int's?
Let's even simplify this. Supposing I have
val hlist = 1 :: 2 :: 3 :: HNil
val tuple = (4, 5, 6)
hlist.zipWithIndex.map(m)
What would be the definition of m to produce, say, a hlist of (1*4) :: (2*5) :: (3*6) :: HNil? I know I could directly zip hlist with tuple but in this scenario I think I do need to pick tuple elements one by one by their positions.
Try to replace sumAgg with type classes.
import shapeless.{::, HList, HNil, Nat, Poly1, Succ}
import shapeless.nat._
import shapeless.poly.->
import shapeless.syntax.std.tuple._
import shapeless.ops.hlist.Tupler
import shapeless.ops.tuple.{At, Length, Mapper}
import slick.lifted.{Query, Rep, Shape}
import slick.jdbc.PostgresProfile.api._
trait MkHList[Agg <: Product, N <: Nat] {
type Out <: HList
def apply(q: Query[Agg, _, Seq]): Out
}
object MkHList {
type Aux[Agg <: Product, N <: Nat, Out0 <: HList] = MkHList[Agg, N] { type Out = Out0 }
def instance[Agg <: Product, N <: Nat, Out0 <: HList](f: Query[Agg, _, Seq] => Out0): Aux[Agg, N, Out0] = new MkHList[Agg, N] {
override type Out = Out0
override def apply(q: Query[Agg, _, Seq]): Out = f(q)
}
implicit def zero[Agg <: Product]: Aux[Agg, _0, HNil] = instance(_ => HNil)
implicit def succ[Agg <: Product, N <: Nat, A](implicit
tailMkHList: MkHList[Agg, N],
at: At.Aux[Agg, N, Rep[A]],
shape: Shape[_ <: FlatShapeLevel, Rep[A], A, Rep[A]]
): Aux[Agg, Succ[N], Query[Rep[A], A, Seq] :: tailMkHList.Out] =
instance(q => q.map(_.at[N]) :: tailMkHList(q))
}
trait SumAgg[Agg <: Product] {
def apply(q: Query[Agg, _, Seq]): Agg
}
object SumAgg {
implicit def mkSumAgg[Agg <: Product, N <: Nat, L <: HList, Tpl <: Product](implicit
length: Length.Aux[Agg, N],
mkHList: MkHList.Aux[Agg, N, L],
tupler: Tupler.Aux[L, Tpl],
mapper: Mapper.Aux[Tpl, sum.type, Agg]
): SumAgg[Agg] = q => tupler(mkHList(q)).map(sum)
}
def sumAgg[Agg <: Product](q: Query[Agg, _, Seq])(implicit sumAggInst: SumAgg[Agg]): Agg = sumAggInst(q)
type Agg = (Rep[Int], Rep[Int], Rep[Int])
sumAgg(??? : Query[Agg, _, Seq]): Agg
Answering your simplified version:
import shapeless._
import syntax.std.tuple._ // brings implicits for tuple.productElements
// defines your polymorphic mapper
object MulPoly extends Poly1 {
// you're going to need one case for each pair of types that you might face.
// E.g. (Int, Int), (Int, String), (String, String), etc.
implicit val intPairCase: Case.Aux[(Int, Int), Int] = at({ case (a, b) => a * b })
}
val hlist = 1 :: 2 :: 3 :: HNil
val tuple = (4, 5, 6)
val tupleHList = tuple.productElements
>>> tupleHList: Int :: Int :: Int :: shapeless.HNil = 4 :: 5 :: 6 :: HNil
hlist
.zip(tupleHList) // intermediate result here: (1, 4) :: (2, 5) :: (3, 6) :: HNil
.map(MulPoly) // map MulPoly over the HList
>>> res0: Int :: Int :: Int :: shapeless.HNil = 4 :: 10 :: 18 :: HNil
So, essentially, your m is a Poly1, that can map on 2-tuples of types that constitute your hlist and a tuple.
You might want to consult with Type Astronaut's Guide to Shapeless, Section 7 about ploymorphic mapping, and this stackoverflow question about how to get an hlist out of any tuple.
I am trying to write a function that given an arbitrary case class replaces the value of the first field with the given new value, using Shapeless.
So far I have the following:
def replaceHead[T, H, R >: H :: HList](obj: T, newHead: H)(implicit generic: Generic.Aux[T, R]): T =
generic.to(obj) match {
case h :: t => generic.from(newHead :: t)
}
If I don't put any type restrictions on R, I cannot use generic.from(newHead :: t) because it understandably expects a subtype of R. If I put R >: H :: HList, I get the following error:
Error:(19, 22) could not find implicit value for parameter generic: shapeless.Generic.Aux[Test.Record,String :: shapeless.HList]
println(replaceHead(Record("abc", 123), "def"))
The only way that I could get it to "work" is with an ugly hack like this that is not type safe:
def replaceHead[T, H, R](obj: T, newHead: H)(implicit generic: Generic.Aux[T, R]): T =
generic.to(obj) match {
case h :: t => generic.from((newHead :: t).asInstanceOf[R])
}
replaceHead(Record("abc", 123), "def") // Works
replaceHead(Record("abc", 123), 456) // Crashes at runtime
I understand that the root problem is because R ends up being for instance String :: Int :: HNil which is String :: HList but String :: HList is not necessarily String :: Int :: HNil, but I can't find a way to access the head of the generic representation without erasing the type signature of the tail.
The following should do the trick,
def replaceHead[C, R <: HList, H, T <: HList](c: C, newHead: H)
(implicit
gen: Generic.Aux[C, R],
ev1: (H :: T) =:= R,
ev2: R =:= (H :: T)
): C = gen.from(newHead :: gen.to(c).tail)
scala> replaceHead(Foo(23, "foo"), 13)
res0: Foo = Foo(13,foo)
We need to two type equalities to prove that the representation type R is same as H :: T in both directions.
This is a good question and one I'm not convinced I've found the simplest solution for. But this does work:
trait NonEmptyHList[T, Head] {
def replaceHead(t: T, h: Head): T
}
implicit def nonEmptyHList[H, Rest <: HList]: NonEmptyHList[H :: Rest, H] = new NonEmptyHList[H :: Rest, H] {
def replaceHead(t: H :: Rest, h: H): H :: Rest = t match {
case _ :: rest => h :: rest
}
}
def replaceHead[T, H, R](obj: T, h: H)(implicit generic: Generic.Aux[T, R], nonEmpty: NonEmptyHList[R, H]): T = {
generic.from(nonEmpty.replaceHead(generic.to(obj), h))
}
val rec = new Record("hello", 1)
replaceHead(rec, "hi") // Record("hi", 1)
replaceHead(rec, 2) // compile error
I'm searching a way to zip two HList together. The first one is generated from a case class converted in its generic representation, and the second one is defined manually as an HList of Nat.
As a result, I expect a tuple (or 2-members HList) with one field from the case class, and the Nat associated.
The goal is to create a "customizable" ZipWithIndex.
def derive[A, I <: HList, R <: HList, Z <: HList](implicit
gen: Generic.Aux[A, R],
zipper: Zip.Aux[R, I, Z],
enc: Lazy[Encoder[Z]])(a: A): Deriver[A] = {
val genRepr = gen.to(A)
val zipped = zip(genRepr :: ??? :: HNil)
enc.value(zipped)
}
case class Foo(a: String, b: String, c: String)
derive[Foo, Nat._1 :: Nat._3 :: Nat.7 :: HNil]
The result will have to match an encoderTuple[H, N <: Nat, T <: HList]: Encoder[(H, N) :: T] or and encoderHList[H, N <: Nat, T <: HList]: Encoder[(H::N::HNil) :: T].
The goal is to create a "customizable" ZipWithIndex.
I guess standard shapeless.ops.hlist.Zip should be enough for that:
trait Encoder[Z] {
type Out
def apply(z: Z): Out
}
object Encoder {
type Aux[Z, Out0] = Encoder[Z] { type Out = Out0 }
// implicits
}
trait Deriver[A]
object Deriver {
// implicits
}
def derive[A, I <: HList, R <: HList, Z <: HList](a: A, i: I)(implicit
gen: Generic.Aux[A, R],
zipper: Zip.Aux[R :: I :: HNil, Z],
enc: Lazy[Encoder.Aux[Z, Deriver[A]]]): Deriver[A] = {
val genRepr: R = gen.to(a)
val zipped: Z = zipper(genRepr :: i :: HNil)
enc.value(zipped)
}
case class Foo(a: String, b: String, c: String)
// derive[Foo, Nat._1 :: Nat._3 :: Nat._7 :: HNil, ???, ???]
derive(Foo("aaa", "bbb", "ccc"), Nat._1 :: Nat._3 :: Nat._7 :: HNil)
I would like to create the equivalent of:
def toTupleN[A1, ..., AN, L <: HList](l: L): TupleN[A1, ..., AN]
Code using toTupleN should compile only when there is exactly one N combination of values in l that the tuple could be created from. Anything else should generate a compile-time error. Available implicit conversions should be taken into account. Note that there are no restrictions on the size of l or the ordering of values in it.
Example:
val l = 23 :: (1, "wibble") :: (2, "wobble") :: "foo" :: HNil
// l: shapeless.::[Int,shapeless.::[(Int, String),shapeless.::[(Int, String),shapeless.::[String,shapeless.HNil]]]] = 23 :: (1,wibble) :: (2,wobble) :: foo :: HNil
val t2: (String, Int) = toTuple2(l)
// t2: (String, Int) = (foo,23)
val nope: (String, String) = toTuple2(l)
// Compiler error because no combination of l's values can create nope
val nein: ((Int, String)) = toTuple2(l)
// Another compiler error because there is more than one way l's values can create nein
This question arose from the answer to the following question. The more general machinery in this question could be used to both create data structures and call any standard function (whose arguments are of different types) using FunctionN#tupled.
Update:
Some examples to define the desired behavior with subtypes:
trait A
trait B extends A
trait C extends A
val a: A
val b: B
val c: C
toTuple2[(A, Int)](5 :: b :: HNil) // (b, 5): subtypes match supertypes when there is no exact match
toTuple2[(A, Int)](5 :: b :: a :: HNil) // (a, 5): only one exact match is available
toTuple2[(A, Int)](5 :: a :: a :: HNil) // compile error: more than one exact match is available
toTuple2[(A, Int)](5 :: b :: c :: HNil) // compile error: more than one inexact match is available
I haven't been able to make target type inference work out quite the way you wanted, but as compensation I've generalized to an arbitrary product type via shapeless's Generic,
import shapeless._, ops.hlist._, test._
object Demo {
trait UniqueSelect[L <: HList, M <: HList] {
def apply(l: L): M
}
object UniqueSelect {
implicit def hnil[L <: HList]: UniqueSelect[L, HNil] =
new UniqueSelect[L, HNil] {
def apply(l: L): HNil = HNil
}
implicit def hcons[L <: HList, H, T <: HList, S <: HList]
(implicit
pt: Partition.Aux[L, H, H :: HNil, S],
ust: UniqueSelect[S, T]
): UniqueSelect[L, H :: T] =
new UniqueSelect[L, H :: T] {
def apply(l: L): H :: T = {
val (h :: HNil, s) = pt(l)
h :: ust(s)
}
}
}
def toProductUniquely[P <: Product] = new ToProductUniquely[P]
class ToProductUniquely[P <: Product] {
def apply[L <: HList, M <: HList](l: L)
(implicit gen: Generic.Aux[P, M], up: UniqueSelect[L, M]): P =
gen.from(up(l))
}
val l = 23 :: (1, "wibble") :: (2, "wobble") :: "foo" :: HNil
val t2 = toProductUniquely[(String, Int)](l)
typed[(String, Int)](t2)
assert(t2 == ("foo", 23))
illTyped("""
toProductUniquely[(String, String)](l)
""")
illTyped("""
toProductUniquely[Tuple1[(Int, String)]](l)
""")
}
Update 1
Adding support for the selection being satisfied by subtypes of the requested types is fairly straightforward if we say that where we have types A and B <: A then the selection of A from A :: B :: HNil is ambiguous because both elements conform to A. This can be done by adding a SubtypeUnifier to the witnesses in the previous definition of hcons,
import shapeless._, ops.hlist._, test._
object Demo extends App {
trait UniqueSelect[L <: HList, M <: HList] {
def apply(l: L): M
}
object UniqueSelect {
implicit def hnil[L <: HList]: UniqueSelect[L, HNil] =
new UniqueSelect[L, HNil] {
def apply(l: L): HNil = HNil
}
implicit def hcons[L <: HList, M <: HList, H, T <: HList, S <: HList]
(implicit
su: SubtypeUnifier.Aux[L, H, M],
pt: Partition.Aux[M, H, H :: HNil, S],
upt: UniqueSelect[S, T]
): UniqueSelect[L, H :: T] =
new UniqueSelect[L, H :: T] {
def apply(l: L): H :: T = {
val (h :: HNil, s) = pt(su(l))
h :: upt(s)
}
}
}
def toProductUniquely[P <: Product] = new ToProductUniquely[P]
class ToProductUniquely[P <: Product] {
def apply[L <: HList, M <: HList](l: L)
(implicit gen: Generic.Aux[P, M], up: UniqueSelect[L, M]): P =
gen.from(up(l))
}
class A
class B extends A
class C
val ac = new A :: new C :: HNil
val bc = new B :: new C :: HNil
val abc = new A :: new B :: new C :: HNil
// Exact match
val tac = toProductUniquely[(A, C)](ac)
typed[(A, C)](tac)
// Subtype
val tbc = toProductUniquely[(A, C)](bc)
typed[(A, C)](tbc)
// Exact match again
val tabc = toProductUniquely[(B, C)](abc)
typed[(B, C)](tabc)
// Ambiguous due to both elements conforming to A
illTyped("""
toProductUniquely[(A, C)](abc)
""")
}
Update 2
We can also accommodate a unification semantics which gives preference to exact match and then falls back to a unique subtype as described in your updated question. We do this by combining the instances from the two solutions above: the exact match instance from the first at normal priority and the subtype match instance at low priority,
import shapeless._, ops.hlist._, test._
object Demo extends App {
trait UniqueSelect[L <: HList, M <: HList] {
def apply(l: L): M
}
object UniqueSelect extends UniqueSelect0 {
implicit def hnil[L <: HList]: UniqueSelect[L, HNil] =
new UniqueSelect[L, HNil] {
def apply(l: L): HNil = HNil
}
implicit def hconsExact[L <: HList, H, T <: HList, S <: HList]
(implicit
pt: Partition.Aux[L, H, H :: HNil, S],
upt: UniqueSelect[S, T]
): UniqueSelect[L, H :: T] =
new UniqueSelect[L, H :: T] {
def apply(l: L): H :: T = {
val (h :: HNil, s) = pt(l)
h :: upt(s)
}
}
}
trait UniqueSelect0 {
implicit def hconsSubtype[L <: HList, M <: HList, H, T <: HList, S <: HList]
(implicit
su: SubtypeUnifier.Aux[L, H, M],
pt: Partition.Aux[M, H, H :: HNil, S],
upt: UniqueSelect[S, T]
): UniqueSelect[L, H :: T] =
new UniqueSelect[L, H :: T] {
def apply(l: L): H :: T = {
val (h :: HNil, s) = pt(su(l))
h :: upt(s)
}
}
}
def toProductUniquely[P <: Product] = new ToProductUniquely[P]
class ToProductUniquely[P <: Product] {
def apply[L <: HList, M <: HList](l: L)
(implicit gen: Generic.Aux[P, M], up: UniqueSelect[L, M]): P = gen.from(up(l))
}
trait A
trait B extends A
trait C extends A
val a: A = new A {}
val b: B = new B {}
val c: C = new C {}
// (b, 5): subtypes match supertypes when there is no exact match
toProductUniquely[(A, Int)](5 :: b :: HNil)
// (a, 5): only one exact match is available
toProductUniquely[(A, Int)](5 :: b :: a :: HNil)
// compile error: more than one exact match is available
illTyped("""
toProductUniquely[(A, Int)](5 :: a :: a :: HNil)
""")
// compile error: more than one inexact match is available
illTyped("""
toProductUniquely[(A, Int)](5 :: b :: c :: HNil)
""")
}
I'm using shapeless in Scala, and I'd like to write a function allPairs that will take two HLists and return an HList of all pairs of elements. For example:
import shapeless._
val list1 = 1 :: "one" :: HNil
val list2 = 2 :: "two" :: HNil
// Has value (1, 2) :: (1, "two") :: ("one", 2) :: ("one", "two") :: HNil
val list3 = allPairs(list1, list2)
Any idea how to do this?
Also, I'd like to emphasize I'm looking for a function, not an inlined block of code.
You can't use a for-comprehension or a combination of map and flatMap with function literals here (as the other answers suggest), since these methods on HList require higher rank functions. If you just have two statically typed lists, this is easy:
import shapeless._
val xs = 1 :: 'b :: 'c' :: HNil
val ys = 4.0 :: "e" :: HNil
object eachFirst extends Poly1 {
implicit def default[A] = at[A] { a =>
object second extends Poly1 { implicit def default[B] = at[B](a -> _) }
ys map second
}
}
val cartesianProductXsYs = xs flatMap eachFirst
Which gives us the following (appropriately typed):
(1,4.0) :: (1,e) :: ('b,4.0) :: ('b,e) :: (c,4.0) :: (c,e) :: HNil
Writing a method that will do this with HList arguments is trickier. Here's a quick example of how it can be done (with some slightly more general machinery).
I'll start by noting that we can think of finding the Cartesian product of two ordinary lists as "lifting" a function that takes two arguments and returns them as a tuple into the applicative functor for lists. For example, you can write the following in Haskell:
import Control.Applicative (liftA2)
cartesianProd :: [a] -> [b] -> [(a, b)]
cartesianProd = liftA2 (,)
We can write a polymorphic binary function that corresponds to (,) here:
import shapeless._
object tuple extends Poly2 {
implicit def whatever[A, B] = at[A, B] { case (a, b) => (a, b) }
}
And define our example lists again for completeness:
val xs = 1 :: 'b :: 'c' :: HNil
val ys = 4.0 :: "e" :: HNil
Now we'll work toward a method named liftA2 that will allow us to write the following:
liftA2(tuple)(xs, ys)
And get the correct result. The name liftA2 is a little misleading, since we don't really have an applicative functor instance, and since it's not generic—I'm working on the model of the methods named flatMap and map on HList, and am open to suggestions for something better.
Now we need a type class that will allow us to take a Poly2, partially apply it to something, and map the resulting unary function over an HList:
trait ApplyMapper[HF, A, X <: HList, Out <: HList] {
def apply(a: A, x: X): Out
}
object ApplyMapper {
implicit def hnil[HF, A] = new ApplyMapper[HF, A, HNil, HNil] {
def apply(a: A, x: HNil) = HNil
}
implicit def hlist[HF, A, XH, XT <: HList, OutH, OutT <: HList](implicit
pb: Poly.Pullback2Aux[HF, A, XH, OutH],
am: ApplyMapper[HF, A, XT, OutT]
) = new ApplyMapper[HF, A, XH :: XT, OutH :: OutT] {
def apply(a: A, x: XH :: XT) = pb(a, x.head) :: am(a, x.tail)
}
}
And now a type class to help with the lifting:
trait LiftA2[HF, X <: HList, Y <: HList, Out <: HList] {
def apply(x: X, y: Y): Out
}
object LiftA2 {
implicit def hnil[HF, Y <: HList] = new LiftA2[HF, HNil, Y, HNil] {
def apply(x: HNil, y: Y) = HNil
}
implicit def hlist[
HF, XH, XT <: HList, Y <: HList,
Out1 <: HList, Out2 <: HList, Out <: HList
](implicit
am: ApplyMapper[HF, XH, Y, Out1],
lift: LiftA2[HF, XT, Y, Out2],
prepend : PrependAux[Out1, Out2, Out]
) = new LiftA2[HF, XH :: XT, Y, Out] {
def apply(x: XH :: XT, y: Y) = prepend(am(x.head, y), lift(x.tail, y))
}
}
And finally our method itself:
def liftA2[HF, X <: HList, Y <: HList, Out <: HList](hf: HF)(x: X, y: Y)(implicit
lift: LiftA2[HF, X, Y, Out]
) = lift(x, y)
And that's all—now liftA2(tuple)(xs, ys) works.
scala> type Result =
| (Int, Double) :: (Int, String) ::
| (Symbol, Double) :: (Symbol, String) ::
| (Char, Double) :: (Char, String) :: HNil
defined type alias Result
scala> val res: Result = liftA2(tuple)(xs, ys)
res: Result = (1,4.0) :: (1,e) :: ('b,4.0) :: ('b,e) :: (c,4.0) :: (c,e) :: HNil
Just as we wanted.