Scala 这有点麻烦,我花了一段时间才把所有的隐式未找到的错误处理好,所以我将跳过细节 class Picks[A, AR <: HList](defaults: A)(implicit lgA: LabelledGeneric.Aux[A, AR]) { private val defaultsRec = lgA.to(defaults) object mergeIntoTemplate extends Poly1 { implicit def caseField[K, V](implicit upd: Updater[AR, FieldType[K, V]]) = at[FieldType[K, V]](upd(defaultsRec, _)) } def from[B, BR <: HList, MR <: HList, F <: Poly](options: B) (implicit optionsLG: LabelledGeneric.Aux[B, BR], mapper: ops.hlist.Mapper.Aux[mergeIntoTemplate.type, BR, MR], toList: ops.hlist.ToTraversable.Aux[MR, List, AR] ) = { optionsLG.to(options).map(mergeIntoTemplate).toList.map(lgA.from) } }

Scala 这有点麻烦,我花了一段时间才把所有的隐式未找到的错误处理好,所以我将跳过细节 class Picks[A, AR <: HList](defaults: A)(implicit lgA: LabelledGeneric.Aux[A, AR]) { private val defaultsRec = lgA.to(defaults) object mergeIntoTemplate extends Poly1 { implicit def caseField[K, V](implicit upd: Updater[AR, FieldType[K, V]]) = at[FieldType[K, V]](upd(defaultsRec, _)) } def from[B, BR <: HList, MR <: HList, F <: Poly](options: B) (implicit optionsLG: LabelledGeneric.Aux[B, BR], mapper: ops.hlist.Mapper.Aux[mergeIntoTemplate.type, BR, MR], toList: ops.hlist.ToTraversable.Aux[MR, List, AR] ) = { optionsLG.to(options).map(mergeIntoTemplate).toList.map(lgA.from) } },scala,scalacheck,property-based-testing,Scala,Scalacheck,Property Based Testing,不幸的是,您无法编写新选取(someValidUser)。从(bogudata)开始,因为隐式for映射器需要一个稳定的标识符。另一方面,cp实例可以与其他类型一起重用: case class BogusName(name: String) assert(cp.from(BogusName("")).head == someValidUser.copy(name = "")) 现在它适用于所有类型!并且伪数据必须是类字段的任何子集,所以即使对于类本身也是有效的 case class Addre

不幸的是,您无法编写
新选取(someValidUser)。从(bogudata)
开始,因为隐式for
映射器需要一个稳定的标识符。另一方面,
cp
实例可以与其他类型一起重用:

case class BogusName(name: String)
assert(cp.from(BogusName("")).head == someValidUser.copy(name = ""))
现在它适用于所有类型!并且伪数据必须是类字段的任何子集,所以即使对于类本身也是有效的

case class Address(country: String, city: String, line_1: String, line_2: String) {
  def isValid = Seq(country, city, line_1, line_2).forall(_.nonEmpty)
}

val acp = new Picks(Address("Test country", "Test city", "Test line 1", "Test line 2"))
val invalidAddresses = acp.from(Address("", "", "", ""))
assert(invalidAddresses.forall(!_.isValid))
你可以看到代码运行在

好吧,这是一个很棒的答案,尽管我对Shapess还不太了解,所以我现在正在阅读Shapess,并期待着将其付诸实施。
def thingGen(invalidValueCount: Int): Gen[Thing] = ???
def someTest = forAll(thingGen) { v => invalidV = v.invalidate(1); validate(invalidV) must beFalse }
def thingGen(invalidValueCount: Int): Gen[Thing] = ???
def someTest = forAll(thingGen) { v => v2 = v copy(id = "xxx"); validate(v2) must beFalse }
import shapeless._, record._
import shapeless.labelled.FieldType
import shapeless.ops.record.Updater
case class User(id: String, name: String, about: String, age: Int) {
  def isValid = id.length == 3 && name.nonEmpty && age >= 0
}
val someValidUser = User("oo7", "Frank", "A good guy", 42)
assert(someValidUser.isValid)
case class BogusUserFields(name: String, id: String, age: Int)
val bogusData = BogusUserFields("", "1234", -5)
val userLG = LabelledGeneric[User]
val bogusLG = LabelledGeneric[BogusUserFields]

val validUserRecord = userLG.to(someValidUser)
val bogusRecord = bogusLG.to(bogusData)
val invalidUser1 = userLG.from(validUserRecord + bogusRecord.head)// invalid name
val invalidUser2 = userLG.from(validUserRecord + bogusRecord.tail.head)// invalid ID
val invalidUser3 = userLG.from(validUserRecord + bogusRecord.tail.tail.head) // invalid age

assert(List(invalidUser1, invalidUser2, invalidUser3).forall(!_.isValid))
object polymerge extends Poly1 {
  implicit def caseField[K, V](implicit upd: Updater[userLG.Repr, FieldType[K, V]]) =
    at[FieldType[K, V]](upd(validUserRecord, _))
}

val allInvalidUsers = bogusRecord.map(polymerge).toList.map(userLG.from)
assert(allInvalidUsers == List(invalidUser1, invalidUser2, invalidUser3))
class Picks[A, AR <: HList](defaults: A)(implicit lgA: LabelledGeneric.Aux[A, AR]) {
  private val defaultsRec = lgA.to(defaults)

  object mergeIntoTemplate extends Poly1 {
    implicit def caseField[K, V](implicit upd: Updater[AR, FieldType[K, V]]) =
      at[FieldType[K, V]](upd(defaultsRec, _))
  }

  def from[B, BR <: HList, MR <: HList, F <: Poly](options: B)
    (implicit
      optionsLG: LabelledGeneric.Aux[B, BR],
      mapper: ops.hlist.Mapper.Aux[mergeIntoTemplate.type, BR, MR],
      toList: ops.hlist.ToTraversable.Aux[MR, List, AR]
    ) = {
    optionsLG.to(options).map(mergeIntoTemplate).toList.map(lgA.from)
  }
}
val cp = new Picks(someValidUser)
assert(cp.from(bogusData) == allInvalidUsers)
case class BogusName(name: String)
assert(cp.from(BogusName("")).head == someValidUser.copy(name = ""))
case class Address(country: String, city: String, line_1: String, line_2: String) {
  def isValid = Seq(country, city, line_1, line_2).forall(_.nonEmpty)
}

val acp = new Picks(Address("Test country", "Test city", "Test line 1", "Test line 2"))
val invalidAddresses = acp.from(Address("", "", "", ""))
assert(invalidAddresses.forall(!_.isValid))