如何在Scala中表示对case classe的部分更新?

jwi*_*ndy 12 scala case-class shapeless

我有以下案例类:

case class PropositionContent(title:String,content:String)
Run Code Online (Sandbox Code Playgroud)

我想代表它作为数据的部分修改.

一种方法是创建案例类:

case class PartialPropositionContent(title:Option[String],content:Option[String)
Run Code Online (Sandbox Code Playgroud)

然后是一些方法:

object PropositionContent {

   def append( pc  : PropositionContent
             , ppc : PartialPropositionContent) =
   PropositionContent ( ppc.title.getOrElse(pc.title)
                      , ppc.content.getOrElse(pc.content) )

   def append( ppc  : PartialPropositionContent
             , ppc2 : PartialPropositionContent ):  PartialPropositionContent = {...}

}
Run Code Online (Sandbox Code Playgroud)

但这有点像锅炉架!

我认为一个case class PropositionContent[M[_]](title:M[String],content:M[String])不会真正解决的问题,我不知道如何使用Shapeless解决问题.

你有什么想法吗?

Tra*_*own 9

这是一个使用Shapeless的相对无样板的方法.首先,我们定义相关函数的一些多态版本Option:

import shapeless._

object orElser extends Poly1 {
  implicit def default[A] = at[Option[A]] {
    oa => (o: Option[A]) => oa orElse o
  }
}

object getOrElser extends Poly1 {
  implicit def default[A] = at[Option[A]] {
    oa => (a: A) => oa getOrElse a
  }
}
Run Code Online (Sandbox Code Playgroud)

我们将HList每个元素都表示为一个更新Option,我们可以编写一个append允许我们附加两个更新的方法:

import UnaryTCConstraint._

def append[U <: HList: *->*[Option]#?, F <: HList](u: U, v: U)(implicit
  mapper: MapperAux[orElser.type, U, F],
  zipper: ZipApplyAux[F, U, U]
): U = v.map(orElser).zipApply(u)
Run Code Online (Sandbox Code Playgroud)

最后我们可以update自己编写方法:

def update[T, L <: HList, F <: HList, U <: HList](t: T, u: U)(implicit
  iso: Iso[T, L],
  mapped: MappedAux[L, Option, U],
  mapper: MapperAux[getOrElser.type, U, F],
  zipper: ZipApplyAux[F, L, L]
) = iso from u.map(getOrElser).zipApply(iso to t)
Run Code Online (Sandbox Code Playgroud)

现在我们只需要一些样板(将来这将是没有必要的,这要归功于推理驱动宏):

implicit def pcIso =
  Iso.hlist(PropositionContent.apply _, PropositionContent.unapply _)
Run Code Online (Sandbox Code Playgroud)

我们还将为此特定更新类型定义别名(这不是绝对必要的,但会使以下示例更简洁):

type PCUpdate = Option[String] :: Option[String] :: HNil
Run Code Online (Sandbox Code Playgroud)

最后:

scala> val pc = PropositionContent("some title", "some content")
pc: PropositionContent = PropositionContent(some title,some content)

scala> val u1: PCUpdate = Some("another title") :: None :: HNil
u1: PCUpdate = Some(another title) :: None :: HNil

scala> val u2: PCUpdate = Some("newest title") :: Some("new content") :: HNil
u2: PCUpdate = Some(newest title) :: Some(new content) :: HNil

scala> append(u1, u2)
res0: PCUpdate = Some(newest title) :: Some(new content) :: HNil

scala> update(pc, append(u1, u2))
res1: PropositionContent = PropositionContent(newest title,new content)
Run Code Online (Sandbox Code Playgroud)

这就是我们想要的.


Tva*_*roh 6

刚刚将 Travis 的代码(我不明白)移植到 Shapeless 2.1:

\n\n
import shapeless._\nimport shapeless.ops.hlist._\n\nobject orElser extends Poly1 {\n  implicit def default[A]: Case[Option[A]] { type Result = Option[A] => Option[A] } = at[Option[A]] {\n    oa => (o: Option[A]) => oa orElse o\n  }\n}\n\nobject getOrElser extends Poly1 {\n  implicit def default[A]: Case[Option[A]] { type Result = A => A } = at[Option[A]] {\n    oa => (a: A) => oa getOrElse a\n  }\n}\n\nimport UnaryTCConstraint._\n\ndef append[U <: HList: *->*[Option]#\xce\xbb, F <: HList](u: U, v: U)\n                                                  (implicit mapper: Mapper.Aux[orElser.type, U, F],\n                                                            zipper: ZipApply.Aux[F, U, U]): U =\n  v map orElser zipApply u\n\ndef update[T, L <: HList, F <: HList, U <: HList](t: T, u: U)\n                                                 (implicit gen: Generic.Aux[T, L],\n                                                           mapped: Mapped.Aux[L, Option, U],\n                                                           mapper: Mapper.Aux[getOrElser.type, U, F],\n                                                           zipper: ZipApply.Aux[F, L, L]) =\n  gen from (u map getOrElser zipApply (gen to t))\n
Run Code Online (Sandbox Code Playgroud)\n\n

API 完好无损。

\n

  • _(我不明白)_ =&gt; 似乎有很多无形的代码:) (4认同)