首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >HList的证据保留LUB约束

HList的证据保留LUB约束
EN

Stack Overflow用户
提问于 2015-05-19 09:40:20
回答 1查看 562关注 0票数 9

我想我需要一个HList,它的所有元素都是某种类型的子类型。LUBConstraint似乎是我想要的,实际上它确实限制了这样一个HList的构造--但是我不知道如何再次获得证据,这样我就可以在HList上映射(实际上,遍历,因为它需要一元),并在每个元素上调用一个方法(存在于LUB类型中)。

此外,我希望由遍历操作产生的HList类型与输入HList的类型完全相同。

用例是一种功能性的“侦听器列表”-- HList的所有元素都是“监听器”,必须通知它们“事件”,接受或拒绝它们,并用更新的“内部状态”返回它们自己的新版本。如果这是我所需要的,那么我只需要一个普通的不可变Scala集合。但是,我还希望不使用asInstanceOf直接对单个元素进行类型化访问--这就是尝试使用HList的动机。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-05-19 13:27:07

通常,如果要对HList中的所有元素执行某种操作,则需要在HList上映射一个多态函数值。例如,假设我有以下设置:

代码语言:javascript
复制
trait Listener[L <: Listener[L]] {
  def handle(s: String): Option[L]
}

class FooListener extends Listener[FooListener] {
  def handle(s: String) =
    if (s.size == 3) Some(this) else None
}

class BarListener extends Listener[BarListener ]{
  def handle(s: String) = Some(this)
}

import shapeless._

val listeners = new FooListener :: new BarListener :: HNil

现在,我想向每个侦听器发送一个String并收集结果。如果我只想发送一个固定值,这将很容易:

代码语言:javascript
复制
object event123 extends Poly1 {
  implicit def listener[L <: Listener[L]] = at[L](_.handle("123"))
}

val result = listeners.map(event123)

它将被适当地键入为一个Option[FooListener] :: Option[BarListener] :: HNil。如果我使用无型,我可以对这个HList进行排序

代码语言:javascript
复制
import scalaz._, Scalaz._, shapeless.contrib.scalaz._

val sequenced: Option[FooListener :: BarListener :: HNil] = sequence(result)

或者直接使用traverse

代码语言:javascript
复制
traverse(listeners)(event123)

不幸的是,在如何定义多态函数值方面有一些限制,这意味着部分应用程序不方便,所以如果我们不知道我们在编译时发送的String,这要复杂得多:

代码语言:javascript
复制
object event extends Poly1 {
  implicit def listener[L <: Listener[L]] = at[(L, String)] {
    case (listener, string) => listener.handle(string)
  }
}

traverse(listeners.zip(listeners.mapConst("123")))(event)

在这里,我们已经用字符串压缩了元素,然后映射了一个多态函数,该函数在结果上使用元组。还有其他方法可以使用相同的方法来实现这一点,但这些方法都不太清楚。

一种完全不同的方法是跳过多态函数值并定义一个新的类型类:

代码语言:javascript
复制
trait Notifiable[L <: HList] {
  def tell(s: String)(l: L): Option[L]
}

object Notifiable {
  implicit val hnilNotifiable: Notifiable[HNil] = new Notifiable[HNil] {
    def tell(s: String)(l: HNil) = Some(HNil)
  }

  implicit def hconsNotifiable[H <: Listener[H], T <: HList](implicit
    tn: Notifiable[T]
  ): Notifiable[H :: T] = new Notifiable[H :: T] {
    def tell(s: String)(l: H :: T) = for {
      h <- l.head.handle(s)
      t <- tn.tell(s)(l.tail)
    } yield h :: t
  }
}

def tell[L <: HList: Notifiable](s: String)(l: L) =
  implicitly[Notifiable[L]].tell(s)(l)

然后:

代码语言:javascript
复制
val sequenced: Option[FooListener :: BarListener :: HNil] =
  tell("123")(listeners)

这不那么通用(它只适用于Option,而不是任意的应用程序),但它不需要额外的依赖项来排序,而且可以说,它比跳过圈来部分应用多态函数值稍微混乱一些,因为编译器的奇怪限制。

票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30321971

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档