2016-06-23 86 views
1

我想寫def parse[T <: HList](list: List[String]): Validation[T]list可以是List("fooid", "barid")TFooId :: BarId :: HNil,以及實現String => Validation[FooId]的類型類別Parse[T]。我怎麼會寫parse,解析列表爲T?我不知道如何爲T的每個元素召喚隱式類型類。Parse List [String] into HList

回答

2

我們可以修改shapeless.ops.traversable.FromTraversable的代碼。

我不知道你的Validation類型是什麼,但我用它作爲scalaz.ValidationNel[String, A]以下(大多時候是這樣,我可以用string語法輕易給我一些Parse實例)的別名。

import scalaz.{Validation => _, _}, Scalaz._ 

type Validation[A] = ValidationNel[String, A] 

trait Parse[T] { 
    def apply(s: String): Validation[T] 
} 

object Parse { 
    def fromScalazParse[E <: Exception, T](f: String => scalaz.Validation[E, T]) = 
    new Parse[T] { 
     def apply(s: String): Validation[T] = 
     f(s).leftMap(_.getMessage).toValidationNel 
    } 

    implicit val booleanParse = fromScalazParse(_.parseBoolean) 
    implicit val intParse  = fromScalazParse(_.parseInt) 
    implicit val doubleParse = fromScalazParse(_.parseDouble) 
} 

隨着Parser類型類排序,我們現在可以創建基於FromTraversable一個類型的類來解析List[String],給我們一個Validation[A :: B :: HNil]

import shapeless._ 
import scala.collection.GenTraversable 

trait FromTraversableParsed[Out <: HList] extends Serializable { 
    def apply(l: GenTraversable[String]) : Validation[Out] 
} 

object FromTraversableParsed { 
    def apply[Out <: HList](implicit from: FromTraversableParsed[Out]) = from 

    implicit val hnilFromTraversableParsed = 
    new FromTraversableParsed[HNil] { 
     def apply(l: GenTraversable[String]): Validation[HNil] = 
     if(l.isEmpty) HNil.successNel[String] 
     else "Traversable is not empty".failureNel[HNil] 
    } 

    implicit def hlistFromTraversableParsed[OutH, OutT <: HList](implicit 
    ftpT: FromTraversableParsed[OutT], 
    parseH: Parse[OutH] 
): FromTraversableParsed[OutH :: OutT] = 
    new FromTraversableParsed[OutH :: OutT] { 
     def apply(l : GenTraversable[String]) : Validation[OutH :: OutT] = 
     if(l.isEmpty) "Empty traversable".failureNel[OutH :: OutT] 
     else (parseH(l.head) |@| ftpT(l.tail))(_ :: _) 
    } 
} 

我們可以添加一些語法使用FromTraversableParsed使有點容易:

implicit class ParseStringListOps(val strings: List[String]) extends AnyVal { 
    def parse[L <: HList](implicit ftp: FromTraversableParsed[L]): Validation[L] = 
    ftp(strings) 
} 

現在我們可以這樣做:

List("1", "true", "3.0").parse[Int :: Boolean :: Double :: HNil] 
// Validation[Int :: Boolean :: Double :: HNil] = Success(1 :: true :: 3.0 :: HNil) 
+0

謝謝,這看起來很酷!在gitter頻道中,已經提到https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/ops/hlists.scala#L2815,是否有可能使用它和跳過寫你自己的TC? – Reactormonk

+0

我認爲'LiftAll'可以給我們Parse [Int] :: Parse [Boolean] :: Parse [Double] :: HNil',但我不確定我們如何使用它來解析'List [String] '? –

+0

我根據'List [String] => String :: String :: String :: HNil'的方法計算了一些東西,用'LiftAll'中的'HList'對其進行壓縮,然後通過驗證遍歷它。 – Reactormonk