Я хотел бы написать def parse[T <: HList](list: List[String]): Validation[T]
. list
может быть List("fooid", "barid")
, и T
FooId :: BarId :: HNil
, и класс Parse[T]
, который реализует String => Validation[FooId]
. Как мне написать parse
, который анализирует список в T
? Я не уверен, как вызвать неявные классы для каждого из элементов T
.Список партирования [String] в HList
1
A
ответ
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)
Спасибо, это выглядит довольно круто! В канале gitter упоминается https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/ops/hlists.scala#L2815, можно ли использовать это и пропустить писать свой собственный TC? – Reactormonk
Я думаю, что 'LiftAll' может дать нам' Parse [Int] :: Parse [Boolean] :: Parse [Double] :: HNil', но я не уверен, как мы можем использовать это для разбора 'List [String] '? –
Я понял что-то в строках 'List [String] => String :: String :: String :: HNil', zip, с помощью' HList' из 'LiftAll', затем переходите через него с проверкой. – Reactormonk