首页 > 解决方案 > 在无形中的产品上扩展选项 [_]

问题描述

我正在通过 Shapeless 学习方法,并且我希望实现一个特定的行为,以允许我们的代码以相同的方式扩展可选案例类,无论它们是否存在。基本上:

给定一个Option[N]where 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

我的最终目标是扁平化嵌套案例类的能力,因此我想将其作为一项规则引入,以便 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案例,我需要一些方法,给定一个案例类,获取它的通用 repr,运行它OptionizeHlist,并生成一个完全由Nones 组成的实例,但我不知道从哪里开始。

标签: scalagenericsshapeless

解决方案


请注意,如果您传递了 a None,则类型擦除会导致您无法知道将多少Nones 放入输出列表中。因此,您需要另一个隐式参数才能将该信息保留到运行时。

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
}

斯卡斯蒂


推荐阅读