2016-06-23 5 views
1

Я хотел бы написать def parse[T <: HList](list: List[String]): Validation[T]. list может быть List("fooid", "barid"), и TFooId :: BarId :: HNil, и класс Parse[T], который реализует String => Validation[FooId]. Как мне написать parse, который анализирует список в T? Я не уверен, как вызвать неявные классы для каждого из элементов T.Список партирования [String] в 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', zip, с помощью' HList' из 'LiftAll', затем переходите через него с проверкой. – Reactormonk