scala - HList的通用解压缩

use*_*645 5 scala hlist shapeless

我有以下Scala问题:

编写一个将采用HList列表的函数

List(23 :: “a” :: 1.0d :: HNil, 24 :: “b” :: 2.0d :: HNil)    # this is list of hlists
Run Code Online (Sandbox Code Playgroud)

并返回列表的HList

List[Int](23, 24) :: List[String](“a”, “b") :: List[Double](1.0d, 2.0d) :: HNil # this is hlist of lists
Run Code Online (Sandbox Code Playgroud)

这有点像通用的unzipN.任意HList都可能吗?

谢谢.

Tra*_*own 6

有很多方法可以解决这个问题,定义自定义类型(如Nikita的答案)是一个非常好的方法.不过,我个人认为以下方法更清楚一点.首先让我们将由幺半群组成的异构列表变成一个幺半群:

import shapeless._
import scalaz._, Scalaz._

implicit object hnilMonoid extends Monoid[HNil] {
  val zero = HNil
  def append(f1: HNil, f2: => HNil) = HNil
}

implicit def hconsMonoid[H: Monoid, T <: HList: Monoid] = new Monoid[H :: T] {
  val zero = Monoid[H].zero :: Monoid[T].zero
  def append(f1: H :: T, f2: => H :: T) =
    (f1.head |+| f2.head) :: (f1.tail |+| f2.tail)
}
Run Code Online (Sandbox Code Playgroud)

我使用ScalazMonoid,但你可以很容易地编写你自己的,它只是一种类,证人,一个类型都有一个标识元素的加样操作.至关重要的是,这个例子列表(任何东西)都是连接下的幺半群,空列表作为标识元素.

接下来是一个简单的多态函数,它包含你在列表中给出的任何内容:

object singleton extends Poly1 { implicit def anything[A] = at[A](List(_)) }
Run Code Online (Sandbox Code Playgroud)

然后我们将它们组合在一起:

def unzipN[L <: HList, Out <: HList](hlists: List[L])(implicit
  mapper: ops.hlist.Mapper.Aux[singleton.type, L, Out],
  monoid: Monoid[Out]
): Out = hlists.map(_ map singleton).suml
Run Code Online (Sandbox Code Playgroud)

现在我们可以定义我们的示例:

val myList = List(23 :: "a" :: 1.0d :: HNil, 24 :: "b" :: 2.0d :: HNil)
Run Code Online (Sandbox Code Playgroud)

我们完成了:

scala> println(unzipN(myList))
List(23, 24) :: List(a, b) :: List(1.0, 2.0) :: HNil
Run Code Online (Sandbox Code Playgroud)

通过这种方法,大多数机器都是非常通用的,并且很容易直观地了解每个步骤的作用.请考虑以下简化示例:

val simple = List(1 :: "a" :: HNil, 2 :: "b" :: HNil)
Run Code Online (Sandbox Code Playgroud)

现在simple.map(_ map singleton)只是以下内容:

List(List(1) :: List("a") :: HNil, List(2) :: List("b") :: HNil)
Run Code Online (Sandbox Code Playgroud)

但是我们知道如何"添加"类型的东西List[Int] :: List[String] :: HNil,这要归功于我们顶级的幺半群机器.所以我们可以使用Scalaz来获取列表的总和suml,我们就完成了.

这都是使用Shapeless 2.0,但它在1.2中看起来非常相似.