[英]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.