簡體   English   中英

無形:Generic.Aux

[英]Shapeless: Generic.Aux

我試圖了解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)

GenericFooBar提供的hlist表示是Int :: String :: HNil ,它也是(Int, String)的表示(Int, String)以及我們可以按此順序使用這兩種類型定義的任何其他案例類。

(作為旁注, LabelledGeneric允許我們區分FooBar ,因為它將表示中的成員名稱包括為類型級別的字符串。)

我們通常希望能夠指定case類並讓Shapeless找出(唯一的)泛型表示,並使Repr成為類型成員(而不是類型參數)允許我們非常干凈地執行此操作。 如果hlist表示類型是類型參數,那么find方法也必須具有Repr類型參數,這意味着您將無法僅指定T並且具有Repr推斷。

使Repr成為類型成員是有意義的,因為Repr由第一個類型參數唯一確定。 想象一下像Iso[A, B]這樣的類型類Iso[A, B]它見證了AB是同構的。 這個類型類與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
                   ^

當無形的Generic.materialize宏創建的Generic[Foo]我們要求的情況下,它的靜態類型為Generic[Foo] { type Repr = Int :: String :: HNil } ,所以gen的說法,該編譯器的手find1擁有我們需要的所有靜態信息。 問題是我們然后明確地將該類型向上轉換為普通的未定義的Generic[Foo] ,並且從那時起,編譯器不知道該實例的Repr是什么。

Scala的路徑依賴類型為我們提供了一種不忘記細化而不向我們的方法添加其他類型參數的方法。 在你的find2 ,編譯器靜態地知道傳入genRepr ,所以當你說返回類型是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的類型參數TRepr是一個類型成員而不是類型參數,因此我們不必將它包含在所有類型簽名和路徑相關類型中使這成為可能,允許我們跟蹤Repr即使它不在我們的類型簽名中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM