Jua*_*npa 24 generics scala shapeless
我试图了解它是如何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
Run Code Online (Sandbox Code Playgroud)
特定
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]
}
Run Code Online (Sandbox Code Playgroud)
Tra*_*own 52
参与的问题是如何Generic和TypeClass实施以及他们所做的事情有足够的不同,他们是活该独立的问题,所以我会坚持Generic在这里.
Generic提供从案例类(和可能类似的类型)到异构列表的映射.任何案例类都有唯一的hlist表示,但任何给定的hlist都对应于非常非常大量的潜在案例类.例如,如果我们有以下案例类:
case class Foo(i: Int, s: String)
case class Bar(x: Int, y: String)
Run Code Online (Sandbox Code Playgroud)
由Generic两者提供的hlist表示Foo和Baris Int :: String :: HNil,它也是(Int, String)我们可以按此顺序使用这两种类型定义的表示和任何其他案例类.
(作为旁注,LabelledGeneric允许我们区分Foo和Bar,因为它包含表示中的成员名称作为类型级别的字符串.)
我们通常希望能够指定case类并让Shapeless找出(唯一的)泛型表示,并且使Repr类型成员(而不是类型参数)允许我们非常干净地执行此操作.如果hlist表示类型是类型参数,那么您的find方法也必须具有Repr类型参数,这意味着您将无法仅指定T并具有Repr推断.
使Repr类型成员有意义只是因为它Repr是由第一个类型参数唯一确定的.想象一下像Iso[A, B]这样的类型类见证A并且B是同构的.这个类型类非常相似Generic,但A不是唯一的dermine - 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
^
Run Code Online (Sandbox Code Playgroud)
当Shapeless的Generic.materialize宏创建Generic[Foo]我们要求的实例时,它被静态地键入为a 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
Run Code Online (Sandbox Code Playgroud)
总结:Generic有一个T唯一确定其类型成员的类型参数Repr,Repr是一个类型成员而不是类型参数,因此我们不必将它包含在所有类型签名中,并且路径相关类型使这成为可能,允许我们跟踪,Repr即使它不在我们的类型签名中.
| 归档时间: |
|
| 查看次数: |
2461 次 |
| 最近记录: |