2016-06-23 6 views
1

Ich möchte def parse[T <: HList](list: List[String]): Validation[T] schreiben. list könnte List("fooid", "barid") und TFooId :: BarId :: HNil sein, und eine Typklasse Parse[T], die String => Validation[FooId] implementiert. Wie würde ich schreiben parse, die die Liste in T analysiert? Ich bin mir nicht sicher, wie ich die impliziten Typklassen für jedes der Elemente von T aufrufen kann.Analysiere Liste [String] in HList

Antwort

2

Wir können den Code von shapeless.ops.traversable.FromTraversable anpassen.

Ich bin nicht sicher, was Ihre Validation Typ ist, aber ich habe es als Alias ​​für scalaz.ValidationNel[String, A] unten (meist so konnte ich die string Syntax leicht mir einige Parse Instanzen geben).

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) 
} 

Mit der Parser Typklasse sortiert, können wir jetzt eine Art Klasse erstellen, basierend auf FromTraversable eine List[String] zu analysieren und geben Sie uns ein 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))(_ :: _) 
    } 
} 

Wir einige Syntax hinzufügen können, um FromTraversableParsed zu machen mit ein wenig einfacher:

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

Jetzt können wir tun:

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

Danke, das sieht ziemlich cool aus! Im Gitter-Kanal wurde https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/ops/hlists.scala#L2815 erwähnt, wäre es möglich, das zu verwenden und Überspringen Sie Ihre eigene TC schreiben? – Reactormonk

+0

Ich denke 'LiftAll' kann uns' Parse [Int] :: Parse [Boolean] :: Parse [Doppel] :: HNil' geben, aber ich bin mir nicht sicher, wie wir das verwenden können, um eine 'List [String] zu analysieren '? –

+0

Ich habe etwas in der Art von "List [String] => String :: String :: String :: HNil" gefunden, ziehe das mit dem 'HList' von' LiftAll', dann gehe mit der Validierung darüber. – Reactormonk