2016-04-29 60 views
8

我使用隱式def來構建遞歸HList類型,以匹配HList的幾種較高kinded類型。我深受this post的啓發。隱式def的Scala更高kinded類型失敗,並顯示「找不到隱式值」

此代碼是可以正常使用:

sealed trait HList { 
    type Plus[L <: HList] <: HList 
} 

class HNil extends HList { 
    type Plus[L <: HList] = L 

    def ::[T](v: T) = HCons(v, this) 
} 

case class Appender[L1 <: HList, L2 <: HList, R <: HList](fn: (L1, L2) => R) { 
    def apply(l1: L1, l2: L2) = fn(l1, l2) 
} 

object HNil extends HNil 

object HList { 
    def ++[L1 <: HList, L2 <: HList](l1: L1, l2: L2)(implicit f: Appender[L1, L2, L1#Plus[L2]]): L1#Plus[L2] = f(l1, l2) 

    implicit def nilAppender[L <: HList]: Appender[HNil, L, L] = Appender((v: HNil, l: L) => l) 

    implicit def consAppender[T, L1 <: HList, L2 <: HList, R <: HList](implicit f: Appender[L1, L2, R]): Appender[HCons[T, L1], L2, HCons[T, R]] = { 
    Appender[HCons[T, L1], L2, HCons[T, R]]((l1: HCons[T, L1], l2: L2) => HCons(l1.head, f(l1.tail, l2))) 
    } 
} 

case class HCons[T, U <: HList](head: T, tail: U) extends HList { 
    type Plus[L <: HList] = HCons[T, U#Plus[L]] 

    def ::[V](v: V) = HCons(v, this) 
} 

import HList._ 

val hlist1 = 2.0 :: "hi" :: HNil 
val hlist2 = 1 :: HNil 

val sum = ++(hlist1, hlist2) 
println("last element : " : + sum.tail.tail.head) // prints last element : 1" 

現在,我不知道爲什麼,但如果我嘗試在HCons,這只是調用現有HList.++方法添加++方法,這是不工作:

case class HCons[T, U <: HList](head: T, tail: U) extends HList { 
type Plus[L <: HList] = HCons[T, U#Plus[L]] 

    def ::[V](v: V) = HCons(v, this) 

    def ++[L2 <: HList](l2: L2) = HList.++(this,l2) 
} 

我得到這個編譯錯誤:

could not find implicit value for parameter f: Appender[HCons[T,U],L2,HCons[T,U]#Plus[L2]] 

由於HConsHList的子類型,就像由HList。++定義的L1類型,我認爲它是可以的。

我試過這個,但是,這不是更好的工作:

implicit def consAppender[T, L1 <: HList, L2 <: HList, L3, R <: HList](implicit f: Appender[L1, L2, R], ev: L3 <:< HCons[T, L1]): Appender[HCons[T, L1], L2, HCons[T, R]] = { 
    Appender[HCons[T, L1], L2, HCons[T, R]]((l1: L3, l2: L2) => HCons(l1.head, f(l1.tail, l2))) 
    } 

我錯過了什麼?

謝謝:)

+0

我沒有試着去關注你在做什麼,但是第三行中的':HList'是一個紅旗。對於任何事情來說,'HList'都是無用的靜態類型。 –

+0

謝謝,實際上它是由從HList繼承的案例類重載 – Loic

+0

我已經刪除它以減少混淆,但行爲是相同的 – Loic

回答

10

您應該將++方法的定義從此改變:

def ++[L2 <: HList](l2: L2) = HList.++(this,l2) 

這樣:

def ++[L2 <: HList](l2: L2)(implicit f: Appender[HCons[T,U], L2, Plus[L2]]) = HList.++(this,l2) 

編譯器沒有足夠的信息來選擇合適的隱式值在方法定義中,但是當你從外部傳遞appender時,這個例子應該通過:

val hlist1 = 2.0 :: "hi" :: HNil 
val hlist2 = 1 :: HNil 
println(hlist1++hlist2) 

更新1:++方法上HCons,我們稱之爲HList.++方法,其需要一個隱式參數。該參數必須是Appender[HCons[T, U], L2, HCons[T, U#Plus[L2]]]。編譯器可以從HList.consAppender填充此隱式參數,但這又需要另一個類型爲Appender[U, L2, U#Plus[L2]]的隱式參數。 這是編譯器無法發現的參數。知道了這一點,上面的代碼可以簡化爲:

def ++[L2 <: HList](l2: L2)(implicit f: Appender[U, L2, U#Plus[L2]]): Plus[L2] = HList.++(this, l2) 

更新2:編譯器必須在隱含參數在調用點填寫,在我們的例子中HCons.++方法(可以驗證,例如,用​​)。它可以從implicits提供兩個附加器類型中進行選擇:

Appender[HNil, L, L] 
Appender[HCons[T, L1], L2, HCons[T, R]] 

第一個可用於僅當類型參數UHNil,其他只有當UHCons。但是此信息在HCons.++內不可用。它只知道U <: HList但不知道它是哪個實現,因此失敗。

+0

謝謝!它的工作!精彩:) – Loic

+0

更新1版本不工作:無法找到參數f的隱式值:Appender [HCons [T,U],L2,HCons [T,U] #Plus [L2]] – Loic

+0

不確定問題出在哪裏那對我很有用(使用Scala 2.11.6)。我們可以深入挖掘它,但只要第一個版本適合你,我就很高興。 – Mifeet