簡體   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