简体   繁体   中英

How to zip an HList with a plain Scala collection?

How to zip an HList with, let's say, a Scala Stream to produce an HList of pairs? Specifically:

import shapeless._

val a = 1.3 :: true :: "z" :: HNil
val b = Stream.from(1)

val ab: (Double, Int) :: (Boolean, Int) :: (String, Int) :: HNil = ???
trait Zipper[A <: HList, B] {
  type Out <: HList
  def zip(a: A, bs: Stream[B]): Out
}

implicit def hnilZipper[B] = new Zipper[HNil, B] {
  type Out = HNil
  def zip(a: HNil, bs: Stream[B]): HNil = HNil
}

implicit def consZipper[Head, Tail <: HList, B](implicit z: Zipper[Tail, B]) = new Zipper[Head :: Tail, B] {
  type Out = (Head, B) :: z.Out
  def zip(a: Head :: Tail, bs: Stream[B]): Out = {
    (a.head, bs.head) :: z.zip(a.tail, bs.tail)
  }
}

def zip[A <: HList, B](a: A, b: Stream[B])(implicit z: Zipper[A, B]): z.Out = z.zip(a, b)

As Marth notes, there are safety issues and this will fail if the Stream is shorter than the HList. But you could pretty easily modify this to return an Option[(A1, B) :: (A2, B) :: ...] or a (A1, Option[B]) :: (A2, Option[B]) :: ... if that's a concern.

It's possible, but there are restrictions. Mostly you have to specify the desired length of the Scala collection to be zipped. The result will, of course, be the length of the shorter of the two HLists.

import shapeless._
import syntax.std.traversable._  // toHList

val a = 1.3 :: true :: "z" :: HNil  // 3 elements

val short_b = Stream.from(1).take(2).toHList[Int::Int::HNil]
val long_b  = Stream.from(7).take(5).toHList[Int::Int::Int::Int::Int::HNil]

toHList returns an Option[HList] so we'll map over the results to extract the HList to be zipped.

short_b.map(a zip _)  // 2 element result
//res0: Option[(Double, Int) :: (Boolean, Int) :: HNil] =
// Some((1.3,1) :: (true,2) :: HNil)

long_b.map(a zip _)  // 3 element result
//res1: Option[(Double, Int) :: (Boolean, Int) :: (String, Int) :: HNil] =
// Some((1.3,7) :: (true,8) :: (z,9) :: HNil)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM