加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – Shapeless:Generic.Aux

发布时间:2020-12-16 09:40:10 所属栏目:安全 来源:网络整理
导读:我正在试图了解Generic的工作原理(和TypeClass)。 github wiki在示例和文档上非常稀疏。有没有详细的描述Generic和TypeClass的规范博客文章/文档页面? 具体来说,这两种方法有什么区别? def find1[T](implicit gen: Generic[T]): Generic[T] = gendef find
我正在试图了解Generic的工作原理(和TypeClass)。 github wiki在示例和文档上非常稀疏。有没有详细的描述Generic和TypeClass的规范博客文章/文档页面?

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

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

解决方法

泛型和TypeClass如何实现的问题以及他们所做的工作是否足够不同,以至于他们可能会有独立的问题,所以我将坚持使用Generic。

泛型提供了案例类(和潜在的类似类型)到异构列表的映射。任何case类都有一个唯一的hlist表示,但任何给定的hlist对应于一个非常非常大量的潜在案例类。例如,如果我们有以下案例类:

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

通用对于Foo和Bar提供的hlist表示是Int :: String :: HNil,它也是(Int,String)和任何其他case类的表示,我们可以按照这两个类型定义它们。

(作为一个附注,LababelGeneric允许我们区分Foo和Bar,因为它包含在表示中的成员名称作为类型级别的字符串。)

我们通常希望能够指定case类,并让Shapeless计算出(唯一)通用表示形式,并使Repr成为一个类型成员(而不是一个type参数),使我们能够非常干净地执行此操作。如果hlist表示类型是一个类型参数,那么您的find方法也必须有一个Repr类型参数,这意味着您将无法仅指定T并推荐使用Repr。

使Repr类型成员有意义只是因为Repr由第一个类型参数唯一确定。想象一下类似Iso(A,B)的类别,证明A和B是同构的。这个类型类与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
                   ^

当Shapeless的Generic.materialize宏创建我们要求的Generic [Foo]实例时,它被静态地类型为Generic [Foo] {type Repr = Int :: String :: HNil},所以编译器所参与的gen参数find1具有我们需要的所有静态信息。问题是,我们然后明确地将该类型提升到一个普通的旧的未修饰的通用[Foo],从编译器那一点起就不知道该实例的Repr是什么。

Scala的路径依赖类型为我们提供了一种不会在我们的方法中添加另一个类型参数的情况下不要忘记细化的方法。在你的find2中,编译器静态地知道Repr的传入,所以当你说返回类型是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具有唯一确定其类型成员Repr的类型参数T,Repr是类型成员而不是类型参数,因此我们不必将其包含在我们所有的类型签名和路径相关类型中这样做可以让我们跟踪Repr,即使它不是我们的类型签名。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读