scala - HList 的通用解压缩 [英] scala - generic unzip for HList

查看:50
本文介绍了scala - HList 的通用解压缩的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下 Scala 问题:

I have the following Scala problem:

编写一个接受 HList 列表的函数

Write a function that will take a list of HLists

List(23 :: "a" :: 1.0d :: HNil, 24 :: "b" :: 2.0d :: HNil)    # this is list of hlists

并返回列表的HList

and return back HList of Lists

List[Int](23, 24) :: List[String]("a", "b") :: List[Double](1.0d, 2.0d) :: HNil # this is hlist of lists

这有点像通用的 unzipN.任意 HList 是否完全可能?

This is sort of like generic unzipN. Is it at all possible for arbitrary HList?

谢谢.

推荐答案

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

There are lots of ways to solve this problem, and defining a custom type class (as in Nikita's answer) is a perfectly good one. I personally find the following approach a little clearer, though. First let's make any heterogenous list made up of monoids into a monoid:

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)
}

我正在使用 ScalazMonoid,尽管你可以很容易地编写自己的——它只是一个类型类,它见证了一个类型具有一个带有标识元素的类似加法的操作.对于这个例子来说,(任何事物的)列表至关重要的是串联起来的幺半群,以空列表作为标识元素.

I'm using Scalaz's Monoid, although you could pretty easily write your own—it's just a type class that witnesses that a type has a addition-like operation with an identity element. Crucially for this example lists (of anything) are monoids under concatenation, with the empty list as the identity element.

接下来是一个简单的多态函数,它将您提供的任何内容包装在一个列表中:

Next for a simple polymorphic function that wraps whatever you give it in a list:

object singleton extends Poly1 { implicit def anything[A] = at[A](List(_)) }

然后我们将它们联系在一起:

And then we tie it all together:

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

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

Now we can define our example:

val myList = List(23 :: "a" :: 1.0d :: HNil, 24 :: "b" :: 2.0d :: HNil)

我们完成了:

scala> println(unzipN(myList))
List(23, 24) :: List(a, b) :: List(1.0, 2.0) :: HNil

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

With this approach most of the machinery is very general, and it's easy to get an intuition for what each step does. Consider the following simplified example:

val simple = List(1 :: "a" :: HNil, 2 :: "b" :: HNil)

现在 simple.map(_ map singleton) 如下:

List(List(1) :: List("a") :: HNil, List(2) :: List("b") :: HNil)

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

But we know how to "add" things of type List[Int] :: List[String] :: HNil, thanks to our monoid machinery at the top. So we can just take the sum of the list using Scalaz's suml, and we're done.

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

This is all using Shapeless 2.0, but it would look pretty similar in 1.2.

这篇关于scala - HList 的通用解压缩的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆