2015-11-06 50 views
5

我正在研究CSV解析庫(tabulate)。它使用簡單的類型進行編碼/解碼:例如,編碼通過CellEncoder(編碼單個單元)和RowEncoder(編碼整行)的實例完成。爲僅具有一個字段的案例類派生類型類實例

使用不成形,我發現它非常簡單的自動導出以下類型的類實例:

  • RowEncoder[A]如果A是一個案例類,其字段都有CellEncoder
  • RowEncoder[A]如果A是ADT,其替代品全部具有RowEncoder
  • CellEncoder[A]如果A是ADT,其替代品全部具有CellEncoder

的事情是,這最後的一個結果是在現實生活中幾乎完全無用的:一個ADT的替代品幾乎都是case類,我不能爲具有多個字段的情況下,類派生一個CellEncoder

但是,我希望能夠做的,是派生一個CellEncoder案件類別,其中有一個單一字段的類型有CellEncoder。這將包括,例如,Either,scalaz的\/,貓Xor ...

這是我到目前爲止有:

implicit def caseClass1CellEncoder[A, H](implicit gen: Generic.Aux[A, H :: HNil], c: CellEncoder[H]): CellEncoder[A] = 
    CellEncoder((a: A) => gen.to(a) match { 
     case h :: t => c.encode(h) 
    }) 

能正常工作明確適用於:

case class Bar(xs: String) 
caseClass1CellEncoder[Bar, String] 
res0: tabulate.CellEncoder[Bar] = [email protected] 

但我不能隱含起作用,下面的失敗:

implicitly[CellEncoder[Bar]] 
>> could not find implicit value for parameter e: tabulate.CellEncoder[Test.this.Bar] 

我也試過以下,沒有更多的成功:

implicit def testEncoder[A, H, R <: H :: HNil](implicit gen: Generic.Aux[A, R], c: CellEncoder[H]): CellEncoder[A] = 
     CellEncoder((a: A) => gen.to(a) match { 
     case h :: t => c.encode(h) 
     }) 

我缺少的東西?我試圖做甚至可能嗎?

回答

3

這是一個有點棘手得到H正確地推斷,但是你可以用<:<實例做到這一點:

import shapeless._ 

case class CellEncoder[A](encode: A => String) 

implicit val stringCellEncoder: CellEncoder[String] = CellEncoder(identity) 
implicit val intCellEncoder: CellEncoder[Int] = CellEncoder(_.toString) 

case class Bar(xs: String) 

implicit def caseClass1CellEncoder[A, R, H](implicit 
    gen: Generic.Aux[A, R], 
    ev: R <:< (H :: HNil), 
    c: CellEncoder[H] 
): CellEncoder[A] = CellEncoder(
    (a: A) => ev(gen.to(a)) match { 
    case h :: t => c.encode(h) 
    } 
) 

(我爲一個完整的工作示例的目的由一個簡單的CellEncoder。 )

這工作,因爲R可以當編譯器尋找一個Generic.Aux[A, R]情況來推斷,然後可以指導H推理尋找一個ev值時。

+1

我需要長時間思考這個問題,但我可以證實它是有效的。巧合的是,我在Circe的代碼中花了很多時間來理解案例類的自動類型派生,所以謝謝你的答案和Circe! –

+0

我正在努力解決這個問題 - 編寫一個CellDecoder('String => A'),因爲R <:

+0

@NicolasRinaudo一個新問題是適當的。 –

相關問題