问题
Thanks to https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0 I understand how to zip shapeless HLists:
Import some stuff from Shapeless 2.0.0-M1:
import shapeless._
import shapeless.ops.hlist._
import syntax.std.tuple._
import Zipper._
Create two HLists:
scala> val h1 = 5 :: "a" :: HNil
h1: shapeless.::[Int,shapeless.::[String,shapeless.HNil]] = 5 :: a :: HNil
scala> val h2 = 6 :: "b" :: HNil
h2: shapeless.::[Int,shapeless.::[String,shapeless.HNil]] = 6 :: b :: HNil
Zip them:
scala> (h1, h2).zip
res52: ((Int, Int), (String, String)) = ((5,6),(a,b))
Now try to define a function that does the same thing:
scala> def f[HL <: HList](h1: HL, h2: HL) = (h1, h2).zip
f: [HL <: shapeless.HList](h1: HL, h2: HL)Unit
The inferred return type is Unit, and indeed applying f to h1 and h2 does just that:
scala> f(h1, h2)
scala>
Is there a way to define f such that I get ((5,6),(a,b)) back in this case?
Ultimately what I'm trying to do is define a function that zips the two HLists and then maps over them, choosing either _1 or _2 based a coin toss, which would yield another HL.
object mix extends Poly1 {
implicit def caseTuple[T] = at[(T, T)](t =>
if (util.Random.nextBoolean) t._2 else t._1)
}
Which works fine in the REPL:
scala> (h1, h2).zip.map(mix)
res2: (Int, String) = (5,b)
But I'm getting tripped up on the above issue when trying to pull this into a function.
Thanks!
回答1:
You can wrap everything up in one method using the Zip
(or in this case Zip.Aux
) type class:
import shapeless._, shapeless.ops.hlist._
object mix extends Poly1 {
implicit def caseTuple[T] = at[(T, T)](t =>
if (util.Random.nextBoolean) t._2 else t._1)
}
def zipAndMix[L <: HList, Z <: HList](h1: L, h2: L)(implicit
zipper: Zip.Aux[L :: L :: HNil, Z],
mapper: Mapper[mix.type, Z]
) = (h1 zip h2) map mix
Now assuming you have h1
and h2
defined as in the question, you can write this:
scala> zipAndMix(h1, h2)
res0: shapeless.::[Int,shapeless.::[String,shapeless.HNil]] = 5 :: b :: HNil
scala> zipAndMix(h1, h2)
res1: shapeless.::[Int,shapeless.::[String,shapeless.HNil]] = 6 :: a :: HNil
scala> zipAndMix(h1, h2)
res2: shapeless.::[Int,shapeless.::[String,shapeless.HNil]] = 5 :: a :: HNil
And so on. This will work in either 2.0.0-M1 or the latest snapshot, although (as I've noted in a comment above) you may run into confusing issues on the way before this bug was fixed.
回答2:
Given the compiler error and some perusing the tests in hlist.scala, zip is defined this way:
def f[L <: HList, OutT <: HList](l : L)(
implicit transposer : Transposer.Aux[L, OutT],
mapper : Mapper[tupled.type, OutT]) = l.transpose.map(tupled)
And the application of my mix
can be defined this way:
def g[L <: HList](l : L)(
implicit mapper: Mapper[mix.type,L]) = l.map(mix)
The composition does what I was looking for:
scala> g(f(h1 :: h2 :: HNil))
res12: shapeless.::[Int,shapeless.::[String,shapeless.HNil]] = 5 :: b :: HNil
scala> g(f(h1 :: h2 :: HNil))
res13: shapeless.::[Int,shapeless.::[String,shapeless.HNil]] = 6 :: a :: HNil
scala> g(f(h1 :: h2 :: HNil))
res14: shapeless.::[Int,shapeless.::[String,shapeless.HNil]] = 6 :: b :: HNil
来源:https://stackoverflow.com/questions/20447686/inferred-type-of-function-that-zips-hlists