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 int
s 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
Post a Comment