2016-01-24 2 views
3

Внешняя система возвращает Seq [String] (тип DB, выход CSV/json), это оболочка базовых типов: string/numbers. Я бы предпочел работать с моей собственной моделью.Convert scala List [String]/List [Object] в model/HList/tuple

object Converter { 
    type Output = (Int, String, Double) // for instance 
    def convert(values: List[String]): Output 
} 

Очевидно, что я не хочу внедрять метод преобразования каждый раз.

Кажется, что мне нужно что-то более простое, чем http://nrinaudo.github.io/tabulate/tut/parsing.html

Можно ли использовать HList здесь? Подобно преобразованию размера HList (String :: String :: String :: HNil) в модель, определяя только явный тип вывода.

ответ

3

Прежде всего, выход convert метода должен быть Option[Output], или некоторую монаду Output (Try, Either, scalaz.\/, scalaz.Validation и т.д.) в случае, если содержание Seq[String] не может быть преобразован в Output (неправильная длина Seq, ошибки при разборе Int с или Doubles и т.д.)

Возможная реализация с shapeless имели бы, чтобы преобразовать класс типов String с его типом параметра, и вспомогательный класс типов для преобразования HList от String до HList Представление Output с первым стилем.

Вот пример реализации:

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 = I } 

    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 => for { 
     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` to HList `H` 
    ft: FromTraversable[S]  // Generate converter of `List[String]` to HList of Strings `S` 
): Option[Output] = 
    values.toHList[S].flatMap(parse.apply).map(gen.from) 
} 

Это просто обновить эту реализацию, чтобы вернуть ошибки монады выбора (или бросать исключения) вместо возвращения Option

А вот как вы можете использовать:

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 

Он также работает с тематическими классами вместо кортежей:

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