繁体   English   中英

Shapeless:这两种实例派生方法有什么区别?

[英]Shapeless: what the difference between these two approaches of instance derivation?

有人可以向我解释这两种类型类实例派生方法(特别是选项 [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实例吗? 如果我会说第一种方法给了我们更多的灵活性,我是对的吗?

实际上,如果没有右侧和实际实现,很难说。

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

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

顺便说一句, Option[A]Some[A]None.type的副产品( List[A]scala.::[A]Nil.type的副产品),有时更容易导出类型 class对于副产品而不是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. 有何不同?

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

版本 1. 略有不同:

  • 起初你正在寻找原语
  • 然后您尝试推导产品(例如,此处不会处理Option
  • 然后你做了一些奇怪的事情:
    • genericOption假设你创建了一个Option[Repr] ,然后我猜 map 它使用Repr
    • 为了构建Repr ,您使用Option[HNil]并使用productOptionOption中添加类型,如果有人将Option用作字段,这将会中断
    • 所以你通过在特例product2中添加一个Option来“修复”这个问题

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

  • 基元的选项( 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]]的解决方案都更简单、更可靠。

根据您直接放入伴随低优先级隐式的内容,可能需要也可能不需要:

  • 如果您定义了副产品,那么手动支持例如OptionListEither可能会与无形派生的冲突
  • 如果您在其同伴 object 中手动为某些类型隐式实现MyTrait ,那么它将与直接在MyTrait中隐式具有相同的优先级 - 因此,如果它可以使用 shapeless 派生,您可能会发生冲突

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM