Я собираюсь дать решение, которое столь же просто, как вы можете получить учитывая некоторые разумные ограничения относительно безопасности типа (без каких-либо исключений во время выполнения, не во время выполнения отражения и т.д.), используя Shapeless для общего вывода:
import scala.util.Try
import shapeless._
trait Creator[A] { def apply(s: String): Option[A] }
object Creator {
def create[A](s: String)(implicit c: Creator[A]): Option[A] = c(s)
def instance[A](parse: String => Option[A]): Creator[A] = new Creator[A] {
def apply(s: String): Option[A] = parse(s)
}
implicit val stringCreate: Creator[String] = instance(Some(_))
implicit val intCreate: Creator[Int] = instance(s => Try(s.toInt).toOption)
implicit val doubleCreate: Creator[Double] =
instance(s => Try(s.toDouble).toOption)
implicit val hnilCreator: Creator[HNil] =
instance(s => if (s.isEmpty) Some(HNil) else None)
private[this] val NextCell = "^([^,]+)(?:,(.+))?$".r
implicit def hconsCreate[H: Creator, T <: HList: Creator]: Creator[H :: T] =
instance {
case NextCell(cell, rest) => for {
h <- create[H](cell)
t <- create[T](Option(rest).getOrElse(""))
} yield h :: t
case _ => None
}
implicit def caseClassCreate[C, R <: HList](implicit
gen: Generic.Aux[C, R],
rc: Creator[R]
): Creator[C] = instance(s => rc(s).map(gen.from))
}
Эта работа точно так, как указано (хотя заметим, что значения завернуты в Option
представлять тот факт, что t он разбор операция может не):
scala> case class Person(name: String, age: Double)
defined class Person
scala> case class Book(title: String, author: String, year: Int)
defined class Book
scala> case class Country(name: String, population: Int, area: Double)
defined class Country
scala> val amy = Creator.create[Person]("Amy,54.2")
amy: Option[Person] = Some(Person(Amy,54.2))
scala> val fred = Creator.create[Person]("Fred,23")
fred: Option[Person] = Some(Person(Fred,23.0))
scala> val hamlet = Creator.create[Book]("Hamlet,Shakespeare,1600")
hamlet: Option[Book] = Some(Book(Hamlet,Shakespeare,1600))
scala> val finland = Creator.create[Country]("Finland,4500000,338424")
finland: Option[Country] = Some(Country(Finland,4500000,338424.0))
Creator
здесь является классом типа, который свидетельствует о том, что мы можем разобрать строку в данном тип. Мы должны предоставить явные экземпляры для базовых типов, таких как String
, Int
и т. Д., Но мы можем использовать Shapeless для генерации экземпляров для классов case (при условии, что у нас есть экземпляров для всех их типов членов).
Это довольно крутой Тревис! Можете ли вы объяснить эту нотацию бит 'Creator [H :: T]'? Как я могу прочитать этот тип здесь? – marios
Это читает «HList, чья голова имеет тип H и чей хвост имеет тип T». T является либо другим типом уровня cons (H2 :: T2), либо HNil, чтобы обозначить, что в этом списке нет дополнительных значений. –
Спасибо, Николас. HList - бесформенная конструкция? – marios