scala – HList的证据保留LUB约束
我想我需要一个HList,它被约束为所有元素都是某种类型的子类型. LUBConstraint似乎是我想要的,事实上它确实限制了这样一个HList的构造 – 但是我无法看到如何再次获取证据,以便我可以映射(实际上,遍历,因为它需要是monadic )在HList上并调用每个元素上的方法(存在于LUB类型中).
另外,我希望从遍历操作得到的HList的类型与输入HList的类型完全相同. 用例是一种功能性的“监听器列表”–HList的所有元素都是“监听器”,必须通知“事件”,接受或拒绝它们,并返回更新的“内部状态”的新版本.如果这就是我所需要的,那么我可以使用普通的不可变Scala集合.但是我也希望在不使用asInstanceOf的情况下直接键入单个元素 – 因此尝试使用HList的动机. 解决方法
通常,如果您要对HList中的所有元素执行某些操作,则需要在HList上映射多态函数值.例如,假设我有以下设置:
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并收集结果.如果我只想发送固定值,这很容易: object event123 extends Poly1 { implicit def listener[L <: Listener[L]] = at[L](_.handle("123")) } val result = listeners.map(event123) 哪个将被适当地键入为Option [FooListener] :: Option [BarListener] :: HNil.如果我使用shapeless-contrib,我可以对此HList进行排序: import scalaz._,Scalaz._,shapeless.contrib.scalaz._ val sequenced: Option[FooListener :: BarListener :: HNil] = sequence(result) 或者只使用遍历: traverse(listeners)(event123) 不幸的是,如何定义多态函数值意味着部分应用程序不方便,所以如果我们不知道我们在编译时发送的字符串,那就更复杂了: 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) 我们用字符串压缩元素,然后映射一个多元函数,该函数在结果上使用元组.还有其他方法可以使用或多或少相同的方法来实现这一点,但它们都不是非常清楚. 一种完全不同的方法是跳过多态函数值并定义一个新的类型类: 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) 然后: val sequenced: Option[FooListener :: BarListener :: HNil] = tell("123")(listeners) 这不太通用(它只适用于Option,而不是任意的应用程序),但它不需要额外的依赖序列,并且它可以说比通过跳跃来部分应用多态函数值更少混乱,因为奇怪的限制编译器. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |