gpt4 book ai didi

scala - 使用 shapeless 合并同一案例类的两个实例

转载 作者:行者123 更新时间:2023-12-02 07:23:00 24 4
gpt4 key购买 nike

我想将一个类的更新实例合并到一个基本实例中,如果该字段在基本实例中为“空”,则选择更新实例的字段而不是基本实例。下面的示例合并了 baseupdate:

case class Foo(a: Option[Int], b: List[Int], c: Option[Int])

val base = Foo(None, Nil, Some(0))
val update = Foo(Some(3), List(4), None)

merge(base,update) == Foo(Some(3), List(4), Some(0))

我试过这样的:

val g = Generic[Foo]
val aHList = g.to(base)
val bHList = g.to(update)

aHList
.zipWithIndex
.map({
case (l: List, i: Int) => l ++ bHList(i)
case (o: Option, i: Int) => if (!bHList(i).nonEmpty) {
updateHList(i)
} else {
o
}
case (_, i: Int) => updateHList(i)
})

但事实证明,一般的 .to 方法并不输出一个 HList 而是一个 Repr。知道如何实现我的目标吗?

谢谢!

最佳答案

对于像这样的特定任务,创建类型类通常比使用 mapPoly 来更容易。

我们可以如下表示一些 T 的更新:

trait Update[T] {
def apply(base: T, update: T): T
}

现在我们需要定义一些实例。如何更新 ListOption 以及一些实例以派生 Update[Foo] 实例。

import shapeless._

object Update extends Update0 {
def apply[A](implicit update: Lazy[Update[A]]): Update[A] = update.value

implicit def optionUpdate[A]: Update[Option[A]] =
new Update[Option[A]] {
def apply(base: Option[A], update: Option[A]): Option[A] = update orElse base
}

implicit def listUpdate[A]: Update[List[A]] =
new Update[List[A]] {
def apply(base: List[A], update: List[A]): List[A] = base ++ update
}

implicit def hnilUpdate: Update[HNil] =
new Update[HNil] {
def apply(base: HNil, update: HNil): HNil = HNil
}

implicit def hconsUpdate[H, T <: HList](
implicit updateH: Update[H], updateT: Lazy[Update[T]]
): Update[H :: T] =
new Update[H :: T] {
def apply(base: H :: T, update: H :: T): H :: T =
updateH(base.head, update.head) :: updateT.value(base.tail, update.tail)
}
}

trait Update0 {
implicit def genericUpdate[A, G <: HList](
implicit gen: Generic.Aux[A, G], updateG: Lazy[Update[G]]
): Update[A] =
new Update[A] {
def apply(base: A, update: A): A =
gen.from(updateG.value(gen.to(base), gen.to(update)))
}
}

我们可以添加一些语法使其更容易一些:

implicit class UpdateOps[A](val base: A) extends AnyVal {
def update(change: A)(implicit update: Lazy[Update[A]]): A =
update.value(base, change)
}

现在我们可以做:

case class Foo(a: Option[Int], b: List[Int], c: Option[Int])

val base = Foo(None, Nil, Some(0))
val update = Foo(Some(3), List(4), None)

base update update // Foo(Some(3),List(4),Some(0))

我们可以为 cats.SemigroupKscalaz.Plus 定义一个实例,这样我们就可以省略 OptionList 实例,同时获得例如 Update[Vector[Int]] :

import cats.SemigroupK
import cats.implicits._

implicit def semigroupKUpdate[F[_], A](implicit F: SemigroupK[F]): Update[F[A]] =
new Update[F[A]] {
def apply(base: F[A], update: F[A]): F[A] = F.combineK(update, base)
}

关于scala - 使用 shapeless 合并同一案例类的两个实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37980300/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com