问题描述
有人可以解释一下这两种类型类实例派生方法(特别是Option [A])之间的区别吗?
1。
<iframe src="https://www.facebook.com/plugins/video.PHP?href=https%3A%2F%2Fwww.facebook.com%2FCoachWeero%2Fvideos%2F582827245698274%2F&show_text=0&width=560" width="560" height="320" style="border:none;overflow:hidden" scrolling="no" frameborder="0" allowTransparency="true" allowFullScreen="true"></iframe>
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,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,t: Lazy[MyTrait[T]]
): MyTrait[H :: T] = ???
}
个实例吗?
如果我说第一种方法可以给我们一点灵活性,我说的对吗?
解决方法
实际上,如果没有右手边和实际的实现,很难说。
根据您提供的信息,并不能得出两个类型类的行为相同。
例如,在第一种方法中,您考虑了一些特殊情况,因此从理论上讲,您可能会不同地重新定义特殊情况下的一些一般行为。
顺便说一句,Option[A]
是Some[A]
和None.type
的副产品(List[A]
是scala.::[A]
和Nil.type
的副产品)并且有时候,为联产品派生类型类要比为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.有何不同?
在第二个版本中,如果可以为MyTrait[Option[A]]
派生,则可以派生A
,并且可以为原始的/选项/乘积的任何A
派生-所以Option[Option[String]]
,Option[String]
和Option[SomeCaseClass]
应该都可以。如果此SomeCaseClass
包含Option
的字段,或者包含Option
的其他案例类等,它也应该起作用。
版本1.稍有不同:
- 起初,您正在寻找图元
- 然后您尝试推导产品(例如,
Option
不会在此处处理) - 然后您做一些奇怪的事情:
-
genericOption
假设您创建了一个Option[Repr]
,然后我猜它是使用Repr
映射的
- 要构建
Repr
,您需要使用Option[HNil]
在Option
中放入productOption
并在Option
中添加类型,如果有人使用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
,则它的优先级与直接在LowPriorityImplicits
中隐式的优先级相同-因此,如果可以使用无形派生它,则可能会有冲突
因此,将无形隐式变量放在Option[String]
中是有意义的,但是将原语和用于List,Option,Either等的手动编解码器直接放在同伴中。也就是说,除非您定义了一些Option[A]
直接在同伴中隐含,可能与“ A
与隐含[
{
rentalName:
rentalAdress:
favorites:[]
...
}
]
”冲突。
由于我不确定您的确切用例,因此无法确定,但是我可能会采用秒方法,或者很可能采用上面代码段中实现的方法。