![](/img/trans.png)
[英]Scala shapeless Generic.Aux implicit parameter not found in unapply
[英]Shapeless: Generic.Aux
我試圖了解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]
}
Generic
和TypeClass
如何實現以及它們的作用所涉及的問題是不同的,它們可能應該得到單獨的問題,因此我將堅持使用Generic
。
Generic
提供從案例類(和可能類似的類型)到異構列表的映射。 任何案例類都有唯一的hlist表示,但任何給定的hlist都對應於非常非常大量的潛在案例類。 例如,如果我們有以下案例類:
case class Foo(i: Int, s: String)
case class Bar(x: Int, y: String)
Generic
為Foo
和Bar
提供的hlist表示是Int :: String :: HNil
,它也是(Int, String)
的表示(Int, String)
以及我們可以按此順序使用這兩種類型定義的任何其他案例類。
(作為旁注, LabelledGeneric
允許我們區分Foo
和Bar
,因為它將表示中的成員名稱包括為類型級別的字符串。)
我們通常希望能夠指定case類並讓Shapeless找出(唯一的)泛型表示,並使Repr
成為類型成員(而不是類型參數)允許我們非常干凈地執行此操作。 如果hlist表示類型是類型參數,那么find
方法也必須具有Repr
類型參數,這意味着您將無法僅指定T
並且具有Repr
推斷。
使Repr
成為類型成員是有意義的,因為Repr
由第一個類型參數唯一確定。 想象一下像Iso[A, B]
這樣的類型類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
^
當無形的Generic.materialize
宏創建的Generic[Foo]
我們要求的情況下,它的靜態類型為Generic[Foo] { type Repr = Int :: String :: HNil }
,所以gen
的說法,該編譯器的手find1
擁有我們需要的所有靜態信息。 問題是我們然后明確地將該類型向上轉換為普通的未定義的Generic[Foo]
,並且從那時起,編譯器不知道該實例的Repr
是什么。
Scala的路徑依賴類型為我們提供了一種不忘記細化而不向我們的方法添加其他類型參數的方法。 在你的find2
,編譯器靜態地知道傳入gen
的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
即使它不在我們的類型簽名中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.