使用 Shapeless 从 case class 派生新类型

Derive new type from case class using Shapeless

假设我有

case class User(id: Long, name: String, age: Long, email: Option[String])

有什么方法可以创建吗

type UpdateUser = ???.???[User] // update user has same fields as User except all are optional

然后我可以用作

UpdateUser(name = Some("foo"), email = Some("foo@bar.com"))

所以基本上是映射类型

Long         :: String         :: Long         :: Option[String] 
->
Option[Long] :: Option[String] :: Option[Long] :: Option[String]

所以再一次,问题是,有没有办法(即使用 shapeless)创建具有此类字段的派生案例 class(无需编写宏来做到这一点。)

Case 类只是 HList。我们可以基于某种类型的 HList 创建一个新类型吗?

您无法生成 de novo 具体类型,除非使用宏注释。您可以做的是使用类型class 派生具有您想要的转换形状的泛型。

import shapeless._, labelled._

trait RecordAsOption[L] {
  type Out <: HList
}
object RecordAsOption {
  def apply[L](implicit recordAsOption: RecordAsOption[L])
      : Aux[L, recordAsOption.Out] =
    recordAsOption
  type Aux[L, Out0 <: HList] = RecordAsOption[L] { type Out = Out0 }
  implicit def hnilRecordAsOption[L <: HNil]: Aux[L, HNil] =
    new RecordAsOption[L] {
      type Out = HNil
      def apply(l: L) = HNil
    }

  implicit def hconsRecordAsOption[K, V, T <: HList](
      implicit tail: RecordAsOption[T])
      : Aux[FieldType[K, V] :: T, FieldType[K, Option[V]] :: tail.Out] =
    new RecordAsOption[FieldType[K, V] :: T] {
      type Out = FieldType[K, Option[V]] :: tail.Out
    }

  implicit def genericRecordAsOption[T, R](
    implicit lg: LabelledGeneric.Aux[T, R], roa: RecordAsOption[T])
      : Aux[T, roa.Out] =
    new RecordAsOption[T] {
      type Out = roa.Out
    }
}

case class User(id: Long, name: String, age: Long, email: Option[String])
val genericUserUpdate = RecordAsOption[User]
type GenericUserUpdate = genericUserUpdate.Out

您可能想向 RecordAsOption 类型添加一些功能class(鉴于名称,可能是一种类似镜头的方法,将选项表示的增量应用到类型 L), 因为就目前而言,该类型有点无用。在某些方面,记录表示不如 case class 好,但它可以与其他基于无形的东西很好地互操作(例如,使用类似的技术来喷射 -json 会很容易) -shapeless 将 JSON 反序列化为 GenericUserUpdate)