Shapeless:实例导出的这两种方法之间的区别是什么?

问题描述

有人可以解释一下这两种类型类实例派生方法(特别是Option [A])之间的区别吗?

1。

<iframe src="https://www.facebook.com/plugins/video.PHP?href=https%3A%2F%2Fwww.facebook.com%2FCoachWeero%2Fvideos%2F582827245698274%2F&show_text=0&width=560" width="560" height="320" style="border:none;overflow:hidden" scrolling="no" frameborder="0" allowTransparency="true" allowFullScreen="true"></iframe>
trait MyTrait[A] {...}

object MyTrait extends LowPriority {
 // instances for primitives
}

trait LowPriority extends LowestPriority {
 final implicit def generic[A,H <: HList](
    implicit gen: Generic.Aux[A,H],h: Lazy[MyTrait[H]]
  ): MyTrait[A] = ???

  final implicit val hnil: MyTrait[HNil] = ???

  final implicit def product[H,T <: HList](
    implicit h: Lazy[MyTrait[H]],t: Lazy[MyTrait[T]]
  ): MyTrait[H :: T] = ???
}

// special instances for Options
trait LowestPriority {
  implicit def genericoption[A,Repr <: HList](
    implicit gen: Generic.Aux[A,Repr],hEncoder: Lazy[MyTrait[Option[Repr]]]
  ): MyTrait[Option[A]] = ???

  implicit val hnilOption: MyTrait[Option[HNil]] = ???

  implicit def productOption1[H,T <: HList](
    implicit
    head: Lazy[MyTrait[Option[H]]],tail: Lazy[MyTrait[Option[T]]],notOption: H <:!< Option[Z] forSome { type Z }
  ): MyTrait[Option[H :: T]] = ???

  implicit def product2[H,tail: Lazy[MyTrait[Option[T]]
  ): MyTrait[Option[Option[H] :: T]] = ???
}

我尝试了两种方法,它们都可以正常工作,但是我不确定它们在所有情况下都会产生相同的结果(也许我错过了一些东西)。

我们真的需要trait MyTrait[A] {...} object MyTrait extends LowPriority { // instances for primitives } trait LowPriority { // deriving instances for options from existing non-option instances final implicit def forOption[A](implicit instance: MyTrait[A]): MyTrait[Option[A]] = ??? // <<<---- final implicit def generic[A,t: Lazy[MyTrait[T]] ): MyTrait[H :: T] = ??? } 个实例吗? 如果我说第一种方法可以给我们一点灵活性,我说的对吗?

解决方法

实际上,如果没有右手边和实际的实现,很难说。

根据您提供的信息,并不能得出两个类型类的行为相同。

例如,在第一种方法中,您考虑了一些特殊情况,因此从理论上讲,您可能会不同地重新定义特殊情况下的一些一般行为。

顺便说一句,Option[A]Some[A]None.type的副产品(List[A]scala.::[A]Nil.type的副产品)并且有时候,为联产品派生类型类要比为Option[A](或List[A])派生一个类型类。

,

我假设“正确工作”是指“已编译”或“已为某些简单用例工作”。

您的两个示例都涉及通用产品类型,但不涉及通用总和类型,因此没有风险,例如Option[A]可以使用Some[A] :+: None :+: CNil派生,这会产生一些歧义。因此(据我所知),您可以编写第二个版本,例如:

trait MyTrait[A] {...}

object MyTrait extends LowPriority {
 // instances for primitives

// deriving instances for options from existing non-option instances
 final implicit def forOption[A](implicit instance: MyTrait[A]): MyTrait[Option[A]] = ??? 
}

trait LowPriority {
// <<<----
 final implicit def hcons[A,H <: HList](
    implicit gen: Generic.Aux[A,H],h: Lazy[MyTrait[H]]
  ): MyTrait[A] = ???

  final implicit val hnil: MyTrait[HNil] = ???

  final implicit def product[H,T <: HList](
    implicit h: Lazy[MyTrait[H]],t: Lazy[MyTrait[T]]
  ): MyTrait[H :: T] = ???
}

它会正确派生事物。

但是1.和2.有何不同?

在第二个版本中,如果可以为MyTrait[Option[A]]派生,则可以派生A,并且可以为原始的/选项/乘积的任何A派生-所以Option[Option[String]]Option[String]Option[SomeCaseClass]应该都可以。如果此SomeCaseClass包含Option的字段,或者包含Option的其他案例类等,它也应该起作用。

版本1.稍有不同:

  • 起初,您正在寻找图元
  • 然后您尝试推导产品(例如,Option不会在此处处理)
  • 然后您做一些奇怪的事情:
    • genericOption假设您创建了一个Option[Repr],然后我猜它是使用Repr
    • 映射的
    • 要构建Repr,您需要使用Option[HNil]Option中放入productOption并在Option中添加类型,如果有人使用Option作为字段,这会破坏
    • 因此您可以通过在特殊情况下product2前面加上Option来“修复”该问题

我想,您仅针对案例类进行了测试,因为第一个版本不适用于:

  • Option[String]用于基元(Option[Int]Option[Option[String]]或您定义为基元的任何内容)
  • 嵌套选​​项(class MyCustomType object MyCustomType { implicit val myTrait: MyTrait[MyCustomType] } implicitly[Option[MyCustomType]]
  • 自定义定义类型的
  • 选项不是案例类,但具有手动定义的实例:
    implicit def forOption[A](implicit instance: MyTrait[A]): MyTrait[Option[A]]

因此,Option的任何解决方案都更简单,更防弹。

取决于是否需要直接将其放入伴随的低优先级隐式对象:

  • 如果您定义了联产品,则可以手动支持例如ListEitherMyTrait可能与无形派生对象冲突
  • 如果您在其伴随对象中为某些类型手动实现了隐式MyTrait,则它的优先级与直接在LowPriorityImplicits中隐式的优先级相同-因此,如果可以使用无形派生它,则可能会有冲突

因此,将无形隐式变量放在Option[String]中是有意义的,但是将原语和用于List,Option,Either等的手动编解码器直接放在同伴中。也就是说,除非您定义了一些Option[A]直接在同伴中隐含,可能与“ A与隐含[ { rentalName: rentalAdress: favorites:[] ... } ] ”冲突。

由于我不确定您的确切用例,因此无法确定,但是我可能会采用秒方法,或者很可能采用上面代码段中实现的方法。