使用Option生成HList的形状不变的flatmap HList

4

以下是给定的内容

case class A(value:Int)
case class B(value:String)

val h:Option[A] :: A :: Option[B] :: Option[A] :: HNil = Some(A(1)) :: A(2) :: Some(B("two")) :: (None:Option[B]) :: HNil

我该如何获取以下内容?
A(1) :: A(2) :: B("two") :: HNil

以下是我的翻译尝试:

trait a extends Poly1 {
    implicit def any[T] = at[T](_ :: HNil)
}
object f extends a {            
    implicit def some[T] = at[Option[T]](t => if (t.isDefined) t.get :: HNil else HNil)         
}

地图工作原理

h map f

> A(1) :: HNil :: A(2) :: HNil :: B(two) :: HNil :: HNil :: HNil

但是flatMap操作失败了

h flatMap f

> could not find implicit value for parameter mapper: shapeless.ops.hlist.FlatMapper[f.type,shapeless.::[Option[A],shapeless.::[A,shapeless.::[Option[B],shapeless.::[Option[B],shapeless.HNil]]]]]

你的问题有点不清楚。f是什么?flatMap的参数是什么样子的?另外,根据定义,h将被静态类型化为具有Some[A]等元素,这几乎从来没有用过。 - Travis Brown
1个回答

1

最有可能的做法是分别定义SomeNone的不同情况:

trait a extends Poly1 {
  implicit def default[T] = at[T](_ :: HNil)
}
object f extends a {
  implicit def caseSome[T] = at[Some[T]](_.get :: HNil)
  implicit def caseNone = at[None.type](_ => HNil)
}

这也意味着您不能在类型中使用通用的 Option,必须在编译时明确知道每个元素是 Some 还是 None
scala> (Some(A(1)) :: A(2) :: Some(B("two")) :: None :: HNil) flatMap f
res1: shapeless.::[A,shapeless.::[A,shapeless.::[B,shapeless.HNil]]] = A(1) :: A(2) :: B(two) :: HNil

这种区别定义了结果表达式的类型: Some(1):: HNil flatMap f 的类型将为::[Int,HNil],但None :: HNil flatMap f 的类型只是HNil
这种类型信息无法从简单的Option在编译时确定: (x: Option[T]) :: HNil flatMap f应该具有类型::[T,HNil]还是HNil?我们不知道,直到运行程序并查看x的值。
我不确定是否有一些聪明的方法可以做到这一点,并获得一个不透明的HList,但此时您将放弃有关每个元素的确切类型和列表长度的精确类型信息,最好将其转换为普通的List(如果您知道最终结果的确切类型,则稍后可以使用shapeless中的cast)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接