gpt4 book ai didi

scala - 无形:Generic.Aux

转载 作者:行者123 更新时间:2023-12-03 10:50:21 24 4
gpt4 key购买 nike

我想了解如何 Generic有效(还有 TypeClass)。 github wiki 在示例和文档上非常稀少。是否有规范的博客文章/文档页面描述 GenericTypeClass详细?

具体来说,这两种方法有什么区别?:

def find1[T](implicit gen: Generic[T]): Generic[T] = gen
def find2[T](implicit gen: Generic[T]): Generic[T] { type Repr = gen.Repr } = gen

给予
object Generic {
type Aux[T, Repr0] = Generic[T] { type Repr = Repr0 }
def apply[T](implicit gen: Generic[T]): Aux[T, gen.Repr] = gen
implicit def materialize[T, R]: Aux[T, R] = macro GenericMacros.materialize[T, R]
}

最佳答案

所涉及的问题如何GenericTypeClass已实现,并且他们所做的足够不同,以至于他们可能需要单独提出问题,所以我会坚持 Generic这里。
Generic提供从案例类(以及潜在的相似类型)到异构列表的映射。任何案例类都有一个唯一的 hlist 表示,但任何给定的 hlist 都对应于非常非常多的潜在案例类。例如,如果我们有以下案例类:

case class Foo(i: Int, s: String)
case class Bar(x: Int, y: String)

Generic 提供的 hlist 表示两者都适用 FooBarInt :: String :: HNil ,这也是 (Int, String) 的表示以及我们可以按此顺序使用这两种类型定义的任何其他案例类。

(作为旁注, LabelledGeneric 允许我们区分 FooBar ,因为它在表示中包含成员名称作为类型级字符串。)

我们通常希望能够指定 case 类并让 Shapeless 找出(唯一的)通用表示,并制作 Repr类型成员(而不是类型参数)允许我们非常干净地做到这一点。如果 hlist 表示类型是类型参数,那么您的 find方法必须有一个 Repr type 参数也是如此,这意味着您不能仅指定 T并拥有 Repr推断。

制作 Repr类型成员之所以有意义,只是因为 Repr由第一个类型参数唯一确定。想象一个像 Iso[A, B] 这样的类型类见证了 AB是同构的。此类型类与 Generic 非常相似,但是 A不唯一地去角质 B ——我们不能只问“与 A 同构的类型是什么?”——所以让 B 没有用一个类型成员(尽管我们可以,如果我们真的想要—— Iso[A] 只是没有任何意义)。

类型成员的问题在于它们很容易忘记,一旦它们消失了,它们就永远消失了。您的返回类型 find1 的事实未精炼(即不包括类型成员)意味着 Generic它返回的实例几乎没有用。例如,静态类型 res0这里还不如 Any :
scala> import shapeless._
import shapeless._

scala> def find1[T](implicit gen: Generic[T]): Generic[T] = gen
find1: [T](implicit gen: shapeless.Generic[T])shapeless.Generic[T]

scala> case class Foo(i: Int, s: String)
defined class Foo

scala> find1[Foo].to(Foo(1, "ABC"))
res0: shapeless.Generic[Foo]#Repr = 1 :: ABC :: HNil

scala> res0.head
<console>:15: error: value head is not a member of shapeless.Generic[Foo]#Repr
res0.head
^

当无形的 Generic.materialize宏创建 Generic[Foo]我们要求的实例,它被静态输入为 Generic[Foo] { type Repr = Int :: String :: HNil } ,所以 gen编译器传递给 find1 的参数拥有我们需要的所有静态信息。问题是我们然后显式地将该类型向上转换为一个普通的未精炼的 Generic[Foo] ,从那时起,编译器不知道 Repr就是这种情况。

Scala 的路径相关类型为我们提供了一种方法,无需在我们的方法中添加另一个类型参数就不会忘记改进。在您的 find2 ,编译器静态知道 Repr来函 gen ,所以当你说返回类型是 Generic[T] { type Repr = gen.Repr } ,它将能够跟踪该信息:
scala> find2[Foo].to(Foo(1, "ABC"))
res2: shapeless.::[Int,shapeless.::[String,shapeless.HNil]] = 1 :: ABC :: HNil

scala> res2.head
res3: Int = 1

总结一下: Generic有一个类型参数 T唯一确定其类型成员 Repr , Repr是一个类型成员而不是类型参数,因此我们不必将它包含在我们所有的类型签名中,而依赖于路径的类型使这成为可能,允许我们跟踪 Repr即使它不在我们的类型签名中。

关于scala - 无形:Generic.Aux,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33725935/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com