2

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

1.

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, T <: HList](
    implicit
    head: Lazy[MyTrait[Option[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, 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] = ???
}

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

我们真的需要LowestPriority实例吗?如果我说第一种方法给了我们更多的灵活性,我是对的吗?

4

2 回答 2

2

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

从您提供的信息来看,这两个类型类的行为并不相同。

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

顺便说一下,Option[A]is 是Some[A]and None.type(List[A]是 and 的 coproduct scala.::[A])的一个 coproduct ,Nil.type有时为 coproducts 派生一个 type class 比 for Option[A](or List[A]) 更容易。

于 2020-08-19T16:22:35.650 回答
2

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

您的两个示例都处理通用产品类型,但不处理通用总和类型,因此不存在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]]如果您可以派生 for A,则可以派生,并且您可以派生任何A原始/选项/产品 - 所以Option[Option[String]]Option[String]并且Option[SomeCaseClass]应该都可以工作。如果它SomeCaseClass包含为Options 的字段或其他为 s 的案例类等,它也应该工作Option

版本 1. 略有不同:

  • 起初你正在寻找原语
  • 然后您尝试为产品推导(例如Option,此处不会处理)
  • 然后你做了一些奇怪的事情:
    • genericOption假设您创建了一个Option[Repr],然后我猜它使用Repr
    • 为了构建Repr您在using中使用Option[HNil]并预先添加类型,如果有人用作字段,这将中断OptionproductOptionOption
    • 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, List,Either可能会与无形派生的相冲突
  • 如果您MyTrait在其伴生对象中手动为某些类型实现了隐式,那么它将具有与直接在中隐式相同的优先级MyTrait- 所以如果它可以使用 shapeless 派生,您可能会发生冲突

出于这个原因,将无形的隐式放入但原语中是有意义的,并将LowPriorityImplicitsList、Option、Either 等的手动编解码器直接放入伴侣中。也就是说,除非你直接在同伴中定义了一些隐含,这可能与“与隐含的 for ”Option[String]发生冲突。Option[A]A

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

于 2020-08-19T16:36:27.147 回答