2013-08-27 19 views
13

問題

是它在某種程度上可以創造無形的HList的提取,看起來像下面這樣。提取的無形HList模仿解析器串聯

val a ~ _ ~ b = 4 :: "so" :: 4.5 :: HNil 
=> a == 4 && b == 4.5 
  1. 通過~更換::,這應該不是問題。
  2. 擺脫終止HNil。有沒有可能出現的問題?

動機

多少汗水和淚水我設法在點,在下面的代碼工作後到達:

for(
    x1 :: _ :: x2 :: HNil <- (expInt ~ "+" ~ expInt).llE 
) yield (x1 + x2) 

expInt解析在一些單子EInt(expInt ~ "+" ~ expInt).llE的類型是E[Int :: String :: Int :: HNil]

我希望<-左側的圖案以某種方式類似於右側的組合器分析器的構造。

+0

一覽我懷疑的是,該提取語法將是不可能的,但這是一個有價值的目標,我祝你好運! –

+0

順便說一句,我會很感興趣,看看你是如何在你的項目中使用無形的... github鏈接? –

+0

@Miles目前這是一個大混亂。我剛剛將我正在工作的功能提取到自己的庫中,並且正在整理事情。一旦有機會存在,我打算將它放在Github上,以便有人能夠理解它。它將成爲一個庫,用於創建命令行工具(解析器部分)來運行計算實驗(monad部分);像解決不同參數下不同算法的問題。我提醒你我的待辦事項清單。 – ziggystar

回答

35

這可以完成,並有一些有趣的轉折。

第一個典型情況是,爲了匹配使用正確的關聯構造函數構建的結構(即::),您將使用相應的右關聯提取器,否則將按相反的順序分解並綁定提取的元素。不幸的是,右聯合提取器必須與右聯合運算符一樣,以Scala中的:結尾,並且會破壞分析器組合語法,因爲提取器名稱必須是~:而不是普通~。不過,我暫且推遲這一點,並以正確的結合性進行工作。

第二捻是我們所需要的取消應用方法,得到取決於我們是否正在匹配多於兩個元件的HList或恰好兩個元件不同類型的結果(和我們不應該能夠匹配少於這兩個元素的列表)。

如果我們匹配超過兩個元素的列表,我們需要將列表分解爲一個由頭部和尾部組成的對,即,給定l: H :: T其中T <: HList我們必須產生(H, T)類型的值。另一方面,如果我們正好匹配兩個元素的列表,即。的形式E1 :: E2 :: HNil,我們需要將列表分解爲由這兩個元素組成的對,即(E1, E2)而不是頭部和尾部,這將是(E1, E2 :: HNil)

這可以使用完全相同的類型級編程技術來完成,這些編程技術在無形中使用。首先,我們在像這樣的它來定義這是要幹什麼提取的工作,與對應於每個上述兩種情況的實例類型類,

import shapeless._ 

trait UnapplyRight[L <: HList] extends DepFn1[L] 

trait LPUnapplyRight { 
    type Aux[L <: HList, Out0] = UnapplyRight[L] { type Out = Out0 } 
    implicit def unapplyHCons[H, T <: HList]: Aux[H :: T, Option[(H, T)]] = 
    new UnapplyRight[H :: T] { 
     type Out = Option[(H, T)] 
     def apply(l: H :: T): Out = Option((l.head, l.tail)) 
    } 
} 

object UnapplyRight extends LPUnapplyRight { 
    implicit def unapplyPair[H1, H2]: Aux[H1 :: H2 :: HNil, Option[(H1, H2)]] = 
    new UnapplyRight[H1 :: H2 :: HNil] { 
     type Out = Option[(H1, H2)] 
     def apply(l: H1 :: H2 :: HNil): Out = Option((l.head, l.tail.head)) 
    } 
} 

然後我們定義我們的提取,

object ~: { 
    def unapply[L <: HList, Out](l: L) 
    (implicit ua: UnapplyRight.Aux[L, Out]): Out = ua(l) 
} 

然後我們去好,

val l = 23 :: "foo" :: true :: HNil 

val a ~: b ~: c = l 
a : Int 
b : String 
c : Boolean 

到目前爲止,一切都很好。現在讓我們回到關聯性問題。如果我們想用左聯合提取器(即~而不是~:)實現同樣的效果,我們需要改變分解的方式。首先讓我們來解釋我們剛剛使用的正確的聯合提取器語法。表達,

val a ~: b ~: c = l 

相當於,

val ~:(a, ~:(b, c)) = l 

相比之下,左邊的關聯版本,

val a ~ b ~ c = l 

相當於,

val ~(~(a, b), c) = l 

爲了使這作爲的提取器工作我們的無應用類型類必須從最後剝離元素,而不是從列表的開始。我們可以用無形的InitLast型類的幫助下做到這一點,

trait UnapplyLeft[L <: HList] extends DepFn1[L] 

trait LPUnapplyLeft { 
    import ops.hlist.{ Init, Last } 
    type Aux[L <: HList, Out0] = UnapplyLeft[L] { type Out = Out0 } 
    implicit def unapplyHCons[L <: HList, I <: HList, F] 
    (implicit 
     init: Init.Aux[L, I], 
     last: Last.Aux[L, F]): Aux[L, Option[(I, F)]] = 
    new UnapplyLeft[L] { 
     type Out = Option[(I, F)] 
     def apply(l: L): Out = Option((l.init, l.last)) 
    } 
} 

object UnapplyLeft extends LPUnapplyLeft { 
    implicit def unapplyPair[H1, H2]: Aux[H1 :: H2 :: HNil, Option[(H1, H2)]] = 
    new UnapplyLeft[H1 :: H2 :: HNil] { 
     type Out = Option[(H1, H2)] 
     def apply(l: H1 :: H2 :: HNil): Out = Option((l.head, l.tail.head)) 
    } 
} 

object ~ { 
    def unapply[L <: HList, Out](l: L) 
    (implicit ua: UnapplyLeft.Aux[L, Out]): Out = ua(l) 
} 

現在我們就大功告成了,

val a ~ b ~ c = l 
a : Int 
b : String 
c : Boolean