[英]What is the best way to avoid clashing between two typeclass definitions in 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]
並使用productOption
在Option
中添加類型,如果有人將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]]
的解決方案都更簡單、更可靠。
根據您直接放入伴隨低優先級隱式的內容,可能需要也可能不需要:
Option
、 List
、 Either
可能會與無形派生的沖突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.