在Scala常规课程上使用镜头 [英] Using Lenses on Scala Regular Classes

查看:79
本文介绍了在Scala常规课程上使用镜头的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Scala最受欢迎的JSON库具有对案例类进行序列化和反序列化的功能.

Most popular JSON libraries for Scala have the ability to serialize and deserialize to case classes.

不幸的是,在Scala 2.11发布之前,case类可以具有的参数数量受到限制(最多22个).作为解决此问题的方法,可以改用常规类. (例如:如何反序列化使用* non-case *类从带有Scala的JSON中提取?).

Unfortunately, until Scala 2.11 is released, there is a restriction on the number of parameters a case class can have (22 maximum). As a workaround to go over this limit, it is possible to use regular classes instead. (for example: How can I deserialize from JSON with Scala using *non-case* classes?).

但是,这失去了案例类的好处.例如,没有自动生成的副本构造函数,并且镜头不适用于常规类,因此操纵结构变得非常麻烦(除非有人将类中的每个字段都设为var,从而放弃了不变性的好处) ).

However, this loses the benefits of case classes. For example, there is no automatically-generated copy constructor, and lenses don't work with regular classes, so manipulating the structure becomes very cumbersome (unless one makes every field in the class a var, giving up on the benefits of immutability).

是否有一种方法可以使常规类的行为更类似于案例类,以便例如镜头也可以对它们起作用?

Is there a way to make regular classes behave more like case classes so that, for example, lenses would also work on them?

推荐答案

似乎是,通过定义一个复制函数(不幸的是手工创建),常规类可以与镜头一起使用,正如Travis在对该问题的评论中提到的那样. ,上面.

It seems to be that, by defining a copy function (unfortunately by hand), regular classes can work with lenses, as Travis mentioned in his comment to the question, above.

下面是一个有效的概念证明(使用json4s和旧的Scalaz镜头实现的副本,这是从Daniel Sobral对

Below is a proof of concept that works (using json4s and a copy of an old Scalaz lens implementation, borrowed from Daniel Sobral's answer to Cleaner way to update nested structures):

import org.json4s._
import org.json4s.JsonDSL._
import org.json4s.native.JsonMethods._
import native.Serialization.write

class Parent(val name:String, val age:Int, val kids:List[Kid]){
  override def toString() = s"""$name is $age years old, her/his kids are ${kids.mkString(", ")}."""

  def copy(name:String = name, age:Int = age, kids:List[Kid] = kids) = 
    new Parent(name, age, kids)
}

class Kid(val name:String, val age:Int){
  override def toString() = s"""$name ($age)"""

  def copy(name:String = name, age:Int = age) = 
    new Kid(name, age)
}

object TestJson {
  implicit val formats = DefaultFormats

  val json = """{"name":"John", "age":41, "kids":[{"name":"Mary", "age":10}, {"name":"Tom", "age":7}]}"""

  def main(args: Array[String]): Unit = {
    val parentKidsLens = Lens(
      get = (_: Parent).kids, 
      set = (p: Parent, kids: List[Kid]) => p.copy(kids = kids))

    val firstKidLens = Lens(
      get = (_: List[Kid]).head,
      set = (kds: List[Kid], kid: Kid) => kid :: kds.tail)

    val kidAgeLens = Lens(
      get = (_: Kid).age,
      set = (k: Kid, age: Int) => k.copy(age = age))

    val parentFirstKidAgeLens = parentKidsLens andThen firstKidLens andThen kidAgeLens


    println( parentFirstKidAgeLens.mod(parse(json).extract[Parent], age => age + 1) )    
  }
}

case class Lens[A,B](get: A => B, set: (A,B) => A) extends Function1[A,B] with Immutable {
  def apply(whole: A): B   = get(whole)
  def updated(whole: A, part: B): A = set(whole, part)
  def mod(a: A, f: B => B) = set(a, f(this(a)))
  def compose[C](that: Lens[C,A]) = Lens[C,B](
    c => this(that(c)),
    (c, b) => that.mod(c, set(_, b))
  )
  def andThen[C](that: Lens[B,C]) = that compose this
}

这篇关于在Scala常规课程上使用镜头的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆