在 Shapeless 的产品上扩展 Option[_]
Expanding Option[_] over a Product in Shapeless
我正在通过 Shapeless 学习我的方式,并且我有一个特定的行为我希望实现以允许我们的代码扩展可选的 case classes,无论它们是否存在。基本上:
给定一个 Option[N]
,其中 N <: Product
,我想生成一个 HList
,其类型与 Generic[N].to
生成的类型相同,但每个元素都包含在一个Option
如果还没有。例如:
case class Foo(a: String, b: Option[Long], c: Option[String])
optionalize[Foo](Some(Foo("abc", Some(123L), None)))
// => Some("abc") :: Some(123L) :: None :: HNil
optionalize[Foo](None)
// => None :: None :: None :: HNil
// where optionalize.Out = Option[String] :: Option[Long] :: Option[String] :: HNil
我的最终目标是能够使嵌套的 case classes 变平,所以我想将其作为规则介绍,以便 Shapeless 可以通过类型推断自动执行此操作。在我看来最大的障碍是理解如何编写 None
案例。目前,我的代码看起来像这样:
trait LowPriEnsureOptional extends Poly1 {
implicit def somethingCase[In]: Case.Aux[In, Option[In]] = at(thing => Some(thing))
implicit val hnilCase: Case.Aux[HNil, HNil] = at(identity)
}
object EnsureOptional extends LowPriEnsureOptional {
implicit def optionCase[In <: Option[_]]: Case.Aux[In, In] = at(identity)
}
object OptionizeHlist {
def optionizeCaseClass[
CC <: Product,
R <: HList
](occ: Option[CC])(
implicit gen: Generic[CC] { type Repr = R },
optionalize: Mapper[EnsureOptional.type, R]
): optionalize.Out =
occ match {
case Some(cc) => optionalize.apply(gen.to(cc))
case None => ???
}
}
为了写出 None
案例,我需要一些方法,给定一个案例 class,得到它的通用 repr,运行 它通过 OptionizeHlist
,并且生成一个完全由 None
组成的实例,但我不知道从哪里开始。
请注意,如果向您传递 None
,类型擦除会使您无法知道要将多少 None
放入输出列表中。因此,您需要另一个隐式参数来将该信息保存到运行时。
final class AllNoneable[H <: HList] private (val allNone: H) extends AnyVal
object AllNoneable {
implicit val allNoneableHNil = new AllNoneable[HNil](HNil)
implicit def allNoneableCons[H >: None.type, T <: HList](implicit t: AllNoneable[T])
= new AllNoneable[H :: T](None :: t.allNone)
}
此外,hnilCase
什么都不做。
trait LowPriEnsureOptional extends Poly1 {
implicit def somethingCase[In]: Case.Aux[In, Option[In]] = at(Some(_))
}
object EnsureOptional extends LowPriEnsureOptional {
implicit def optionCase[In <: Option[_]]: Case.Aux[In, In] = at(identity)
}
def optionalizeCase
[C, Rep <: HList, Opt <: HList](c: Option[C])
(implicit
gen: Generic.Aux[C, Rep], opt: Mapper.Aux[EnsureOptional.type, Rep, Opt],
nones: AllNoneable[Opt]): Opt
= c match {
case Some(c) => opt.apply(gen.to(c))
case None => nones.allNone
}
我正在通过 Shapeless 学习我的方式,并且我有一个特定的行为我希望实现以允许我们的代码扩展可选的 case classes,无论它们是否存在。基本上:
给定一个 Option[N]
,其中 N <: Product
,我想生成一个 HList
,其类型与 Generic[N].to
生成的类型相同,但每个元素都包含在一个Option
如果还没有。例如:
case class Foo(a: String, b: Option[Long], c: Option[String])
optionalize[Foo](Some(Foo("abc", Some(123L), None)))
// => Some("abc") :: Some(123L) :: None :: HNil
optionalize[Foo](None)
// => None :: None :: None :: HNil
// where optionalize.Out = Option[String] :: Option[Long] :: Option[String] :: HNil
我的最终目标是能够使嵌套的 case classes 变平,所以我想将其作为规则介绍,以便 Shapeless 可以通过类型推断自动执行此操作。在我看来最大的障碍是理解如何编写 None
案例。目前,我的代码看起来像这样:
trait LowPriEnsureOptional extends Poly1 {
implicit def somethingCase[In]: Case.Aux[In, Option[In]] = at(thing => Some(thing))
implicit val hnilCase: Case.Aux[HNil, HNil] = at(identity)
}
object EnsureOptional extends LowPriEnsureOptional {
implicit def optionCase[In <: Option[_]]: Case.Aux[In, In] = at(identity)
}
object OptionizeHlist {
def optionizeCaseClass[
CC <: Product,
R <: HList
](occ: Option[CC])(
implicit gen: Generic[CC] { type Repr = R },
optionalize: Mapper[EnsureOptional.type, R]
): optionalize.Out =
occ match {
case Some(cc) => optionalize.apply(gen.to(cc))
case None => ???
}
}
为了写出 None
案例,我需要一些方法,给定一个案例 class,得到它的通用 repr,运行 它通过 OptionizeHlist
,并且生成一个完全由 None
组成的实例,但我不知道从哪里开始。
请注意,如果向您传递 None
,类型擦除会使您无法知道要将多少 None
放入输出列表中。因此,您需要另一个隐式参数来将该信息保存到运行时。
final class AllNoneable[H <: HList] private (val allNone: H) extends AnyVal
object AllNoneable {
implicit val allNoneableHNil = new AllNoneable[HNil](HNil)
implicit def allNoneableCons[H >: None.type, T <: HList](implicit t: AllNoneable[T])
= new AllNoneable[H :: T](None :: t.allNone)
}
此外,hnilCase
什么都不做。
trait LowPriEnsureOptional extends Poly1 {
implicit def somethingCase[In]: Case.Aux[In, Option[In]] = at(Some(_))
}
object EnsureOptional extends LowPriEnsureOptional {
implicit def optionCase[In <: Option[_]]: Case.Aux[In, In] = at(identity)
}
def optionalizeCase
[C, Rep <: HList, Opt <: HList](c: Option[C])
(implicit
gen: Generic.Aux[C, Rep], opt: Mapper.Aux[EnsureOptional.type, Rep, Opt],
nones: AllNoneable[Opt]): Opt
= c match {
case Some(c) => opt.apply(gen.to(c))
case None => nones.allNone
}