shapeless - Convert scala List[String]/List[Object] into model/HList/tuple -


an external system returns seq[string] (kind of db, output csv/json), it's wrap of base types: string/numbers. i'd rather work own model.

object converter {   type output = (int, string, double) // instance    def convert(values: list[string]): output } 

obviously, not want implement convert-method every time.

seems need simpler http://nrinaudo.github.io/tabulate/tut/parsing.html

is possible use hlist here? conversion sized hlist (string:: string:: string:: hnil) model defining explicit output type.

first of all, output of convert method have option[output], or monad of output (try, either, scalaz.\/, scalaz.validation, etc.) in case contents of seq[string] can't converted output (wrong length of seq, errors in parsing ints or doubles, etc.)

a possible implementation shapeless have typeclass convert string parameter type, , auxiliary typeclass convert hlist of string hlist representation of output first typeclass.

here sample implementation:

import shapeless._ import shapeless.syntax.std.traversable._ import shapeless.ops.traversable._  trait parse[out] {   def apply(value: string): option[out] } object parse {   implicit object converttoint extends parse[int] {     def apply(value: string) = try(value.toint).tooption   }    implicit object converttostring extends parse[string] {     def apply(value: string) = some(value)   }    implicit object converttodouble extends parse[double] {     def apply(value: string) = try(value.todouble).tooption   } }  trait parseall[out] {   type in <: hlist   def apply(values: in): option[out] }  object parseall {    type aux[i, o] = parseall[o] { type in = }    implicit object converthnil extends parseall[hnil] {     type in = hnil     def apply(value: hnil) = some(hnil)   }    implicit def converthlist[t, ho <: hlist](implicit      cv: parse[t],      cl: parseall[ho]   ) = new parseall[t :: ho] {     type in = string :: cl.in     def apply(value: in) = value match {       case x :: xs => {         t <- cv(x)         h0 <- cl(xs)       } yield t :: h0     }   } }  trait converter {   type output   def convert[s <: hlist, h <: hlist](values: list[string])(implicit     gen: generic.aux[output, h], // compute hlist representation `h` of output     parse: parseall.aux[s, h],   // generate parser of hlist of string `s` hlist `h`     ft: fromtraversable[s]       // generate converter of `list[string]` hlist of strings `s`   ): option[output] =     values.tohlist[s].flatmap(parse.apply).map(gen.from) } 

it's simple upgrade implementation return error monad of choice (or throw exceptions) instead of returning option

and here how can use it:

scala> object converterisd extends converter {   type output = (int, string, double) }  defined object converterisd  scala> converterisd.convert(list("1", "foo", "2.34")) res0: option[converterisd.output] = some((1,foo,2.34))  scala> converterisd.convert(list("1", "foo", "2.34", "5")) res1: option[converterisd.output] = none  scala> converterisd.convert(list("1", "foo", "bar")) res2: option[converterisd.output] = none 

it works case classes instead of tuples:

scala> case class model(i: int, d: double)  defined class model  scala> object convertermodel extends converter {   type output = model }  defined object convertermodel  scala> convertermodel.convert(list("1", "2.34")) res0: option[convertermodel.output] = some(model(1,2.34))  scala> convertermodel.convert(list("1")) res1: option[convertermodel.output] = none  scala> convertermodel.convert(list("1", "foo"))  res2: option[convertermodel.output] = none 

Comments