0

我有这个示例代码:

import java.util.UUID

import shapeless.LabelledGeneric
import shapeless.record._
import shapeless.syntax.singleton._

object LabelTest extends App {

  case class IncomingThing(name: String, age: Int)
  case class DatabaseIncomingThing(name: String, age: Int, id: UUID)

  val genIncoming = LabelledGeneric[IncomingThing]
  val genDatabase = LabelledGeneric[DatabaseIncomingThing]

  val thing = IncomingThing("John", 42)

  val structuralVersionOfIncomingThing = genIncoming.to(thing)

  val updated = genDatabase.from(structuralVersionOfIncomingThing + ('id ->> UUID.randomUUID()))

  println(updated) // DatabaseIncomingThing(John,42,a45081f2-4ed5-4d2b-8fd9-4d8986875ed7)

}

这很好,因为我不必编写从IncomingThingto复制所有字段的样板DatabaseIncomingThing。但是,我希望不必同时维护这两种类型,因为两者之间有非常明确的关系(一个有id,另一个没有)。

有没有办法通过添加或删除一个字段来从给定的案例类创建类型?我想像

type IncomingThing = withoutField[DatabaseIncomingThing]('id)

或者类似的东西。

4

1 回答 1

0

代替DatabaseIncomingThing

val updated: DatabaseIncomingThing = 
  genDatabase.from(structuralVersionOfIncomingThing + ('id ->> UUID.randomUUID()))

你可以使用 rawHList

val updated1: FieldType[Witness.`'name`.T, String] :: FieldType[Witness.`'age`.T, Int] :: FieldType[Witness.`'id`.T, UUID] :: HNil = 
  structuralVersionOfIncomingThing + ('id ->> UUID.randomUUID())
val updated2: FieldType[Witness.`'name`.T, String] :: FieldType[Witness.`'age`.T, Int] :: HNil = 
  updated1 - 'id

在类型层面

implicitly[Remover.Aux[FieldType[Witness.`'name`.T, String] :: FieldType[Witness.`'age`.T, Int] :: FieldType[Witness.`'id`.T, UUID] :: HNil,
  Witness.`'id`.T,
  (UUID, FieldType[Witness.`'name`.T, String] :: FieldType[Witness.`'age`.T, Int] :: HNil)]]

implicitly[Updater.Aux[
  FieldType[Witness.`'name`.T, String] :: FieldType[Witness.`'age`.T, Int] :: HNil,
  FieldType[Witness.`'id`.T, UUID],
  FieldType[Witness.`'name`.T, String] :: FieldType[Witness.`'age`.T, Int] :: FieldType[Witness.`'id`.T, UUID] :: HNil]]

您可以创建类型类

trait WithoutField[A, K] {
  type Out <: HList
}

object WithoutField {
  type Aux[A, K, Out0 <: HList] = WithoutField[A, K] { type Out = Out0 }
  def instance[A, K, Out0 <: HList]: Aux[A, K, Out0] = new WithoutField[A, K] { type Out = Out0 }

  implicit def mkWithoutField[A, L <: HList, K, T, L1 <: HList](implicit
    labelledGeneric: LabelledGeneric.Aux[A, L],
    remover: Remover.Aux[L, K, (T, L1)]): Aux[A, K, L1] = instance
}

并使用它

def foo[Out <: HList](implicit withoutField: WithoutField.Aux[DatabaseIncomingThing, Witness.`'id`.T, Out]) = {
  // now you can use type Out inside
  type IncomingThing = Out
  ???
}
于 2018-09-28T09:28:57.800 回答