简体   繁体   English

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

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

Can someone explain me what the difference between these two approaches for typeclass instance derivation (specifically for Option[A])?有人可以向我解释这两种类型类实例派生方法(特别是选项 [A])之间的区别吗?

1. 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] = ???
}

I tried both and they worked correctly, but i'm not sure that they will produce the same results for all cases (maybe i've missed something).我都试过了,它们都工作正常,但我不确定它们是否会在所有情况下产生相同的结果(也许我错过了一些东西)。

Do we really need LowestPriority instances for this?为此,我们真的需要LowestPriority实例吗? Am i right if i would say that the first approach gives us just a little bit more flexibility?如果我会说第一种方法给了我们更多的灵活性,我是对的吗?

Actually it's hard to say without right hand sides and actual implementations.实际上,如果没有右侧和实际实现,很难说。

From information you provided it doesn't follow that the two type classes behave equivalently.从您提供的信息来看,这两个类型类的行为并不相同。

For example in the 1st approach you consider some special cases, so theoretically it's possible that you redefine some general behavior in special case differently.例如,在第一种方法中,您考虑了一些特殊情况,因此从理论上讲,您可以在特殊情况下以不同的方式重新定义一些一般行为。

By the way, Option[A] is a coproduct of Some[A] and None.type ( List[A] is a coproduct of scala.::[A] and Nil.type ) and sometimes it's easier to derive a type class for coproducts than for Option[A] (or List[A] ).顺便说一句, Option[A]Some[A]None.type的副产品( List[A]scala.::[A]Nil.type的副产品),有时更容易导出类型 class对于副产品而不是Option[A] (或List[A] )。

I assuming that by "worked correctly" you mean "compiled" or "worked for some simple use case".我假设“工作正常”是指“已编译”或“适用于一些简单的用例”。

Both of your examples deal with generic product types, but not with generic sum types, so there is no risk that eg Option[A] could get derived using Some[A]:+: None:+: CNil , which would enforce some ambiguity.您的两个示例都处理通用产品类型,但不处理通用总和类型,因此不存在例如Option[A]可以使用Some[A]:+: None:+: CNil ,这会造成一些歧义. So (as far as I can tell) you could write the second version like:所以(据我所知)你可以像这样写第二个版本:

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] = ???
}

and it would derive things correctly.它会正确地推导事物。

But how 1. and 2. differs?但是 1. 和 2. 有何不同?

In second version you can derive MyTrait[Option[A]] if you can derive for A , and you can derive for any A which is primitive/option/product - so Option[Option[String]] , Option[String] and Option[SomeCaseClass] should all work.在第二个版本中,如果可以派生A ,则可以派生MyTrait[Option[A]] ,并且可以派生任何原始/选项/产品A - 所以Option[Option[String]]Option[String]Option[SomeCaseClass]应该都可以。 It should also work if this SomeCaseClass contains fields which are Option s, or other case classes which are Option s, etc.如果此SomeCaseClass包含Option s 的字段或其他Option s 的案例类等,它也应该有效。

Version 1. is slightly different:版本 1. 略有不同:

  • at first you are looking for primitives起初你正在寻找原语
  • then you try to derive for a product (so eg Option would not be handled here)然后您尝试推导产品(例如,此处不会处理Option
  • then you do something weird:然后你做了一些奇怪的事情:
    • genericOption assumes that you created a Option[Repr] , and then I guess map it using Repr genericOption假设你创建了一个Option[Repr] ,然后我猜 map 它使用Repr
    • in order to build that Repr you take Option[HNil] and prepend types inside Option using productOption , which would break if someone used Option as a field为了构建Repr ,您使用Option[HNil]并使用productOptionOption中添加类型,如果有人将Option用作字段,这将会中断
    • so you "fix" that by prepending an Option in a special case product2所以你通过在特例product2中添加一个Option来“修复”这个问题

I guess, you tested that only against case classes, because the first version would not work for:我想,您只针对案例类进行了测试,因为第一个版本不适用于:

  • Option for primitives ( Option[String] , Option[Int] or whatever you defined as primitive)基元的选项( Option Option[String]Option[Int]或您定义为基元的任何内容)
  • nested options ( Option[Option[String]] )嵌套选项 ( Option[Option[String]] )
  • options for custom defined types which are not case classes but have manually defined instances:自定义类型的选项,这些类型不是案例类,但具有手动定义的实例:
     class MyCustomType object MyCustomType { implicit val myTrait: MyTrait[MyCustomType] } implicitly[Option[MyCustomType]]

For that reason any solution with implicit def forOption[A](implicit instance: MyTrait[A]): MyTrait[Option[A]] is simpler and more bulletproof.出于这个原因,任何带有implicit def forOption[A](implicit instance: MyTrait[A]): MyTrait[Option[A]]的解决方案都更简单、更可靠。

Depending on what you put directly into companion low-priority implicits might be or might not be needed:根据您直接放入伴随低优先级隐式的内容,可能需要也可能不需要:

  • if you defined coproducts then manual support for eg Option , List , Either could conflict with shapeless derived ones如果您定义了副产品,那么手动支持例如OptionListEither可能会与无形派生的冲突
  • if you manually implemented MyTrait implicit for some type in its companion object then it would have the same priority as implicits directly in MyTrait - so if it could be derived using shapeless you could have conflicts如果您在其同伴 object 中手动为某些类型隐式实现MyTrait ,那么它将与直接在MyTrait中隐式具有相同的优先级 - 因此,如果它可以使用 shapeless 派生,您可能会发生冲突

For that reason it makes sense to put shapeless implicits in LowPriorityImplicits but primitives, and manual codecs for List, Option, Either, etc directly in companion.出于这个原因,在LowPriorityImplicits中放置无形隐式是有意义的,但将 List、Option、Either 等的原语和手动编解码器直接放在 companion 中是有意义的。 That is, unless you defined some eg Option[String] implicits directly in companion which could clash with " Option[A] with implicit for A ".也就是说,除非您在同伴中直接定义了一些例如Option[String]隐含,这可能与“ Option[A] with implicit for A ”发生冲突。

Since I don't know your exact use case I cannot tell for sure, but I would probably go with the seconds approach, or most likely with the one I implemented in the snippet above.由于我不知道您的确切用例,因此我无法确定,但我可能会使用秒方法 go,或者最有可能使用我在上面的代码片段中实现的方法。

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

相关问题 什么是避免无形的两个类型定义之间发生冲突的最佳方法 - What is the best way to avoid clashing between two typeclass definitions in shapeless 在Shapeless中,我可以为非案例类提供一个`LabelledGeneric`实例,它将启用类型类实例的自动派生吗? - In Shapeless, can I supply an instance of `LabelledGeneric` for non case classes that will enable automatic derivation of typeclass instances? Typeless类的通用派生 - Generic Derivation of Typeclass from shapeless-guide 这两个Scala定义有什么区别 - What is the difference between this two Scala definitions 这两种Scala模式之间有什么区别? - What's the difference between these two Scala patterns? 如何从Shapeless记录中提取标签以进行泛型类型类派生 - How to extract label from Shapeless record for generic type class derivation 无形(scala)中的“at”是什么? - What is “at” in shapeless (scala)? Scala / Python中这两个映射表达式之间有什么区别? - What is the difference between these two map expressions in Scala/Python? Scala中这两种映射函数的方式有什么区别? - What is the difference between these two ways of mapping functions in scala? Scala匿名函数中的这两个表达式有什么区别? - What is the difference between these two expressions in a Scala anonymous function?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM