从Strings构造简单的Scala案例类,严格没有样板

Per*_*ing 29 constructor scala boilerplate case-class

我寻求简洁的代码来初始化Strings中的简单Scala案例类(例如csv行):

case class Person(name: String, age: Double)
case class Book(title: String, author: String, year: Int)
case class Country(name: String, population: Int, area: Double)

val amy = Creator.create[Person]("Amy,54.2")
val fred = Creator.create[Person]("Fred,23")
val hamlet = Creator.create[Book]("Hamlet,Shakespeare,1600")
val finland = Creator.create[Country]("Finland,4500000,338424")
Run Code Online (Sandbox Code Playgroud)

Creator这样做最简单的对象是什么?我会从Scala那里学到很多很好的解决方案.

(注意伴侣的对象Person,BookCountry不应被强迫存在.这将是锅炉板!)

Tra*_*own 46

我将给出一个简单的解决方案,给出一些关于类型安全的合理约束(没有运行时异常,没有运行时反射等),使用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))
}
Run Code Online (Sandbox Code Playgroud)

这完全符合指定的工作(尽管注意这些值被包装Option以表示解析操作可能失败的事实):

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))
Run Code Online (Sandbox Code Playgroud)

Creator这是一个类型类,它提供了我们可以将字符串解析为给定类型的证据.我们必须为基本类型,如提供明确的情况下String,Int等等,但我们可以用无形为case类一般派生实例(假设我们有Creator实例为所有的成员类型).

  • 这将读取"头部为H型且尾部为T型"的HList.T是另一个类型级别缺点(H2 :: T2)或HNil,表示该列表中没有其他值. (3认同)
  • @marios是的,它本质上是一个元组,在arity上有一些额外的抽象.当我回到电脑前时,我会添加一些细节. (3认同)
  • @marios我刚刚在[这篇博文]中扩展了这个答案(可能太多了)(https://meta.plasm.us/posts/2015/11/08/type-classes-and-generic-derivation /). (3认同)
  • 这是非常酷的特拉维斯!你能解释一下这个符号`Creator [H :: T]`吗?我如何在这里阅读类型? (2认同)