سؤال

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!

هل كانت مفيدة؟

المحلول

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.

نصائح أخرى

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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top