如何将Monocle Lens打印为属性访问器样式字符串

时间:2017-06-09 10:01:35

标签: scala monocle-scala

使用Monocle我可以定义一个Lens来读取一个没有问题的案例类成员,

    val md5Lens = GenLens[Message](_.md5)

这可用于比较两个对象之间md5的值,并在值不同时使用包含字段名称的错误消息失败。

有没有办法从Lens单独生成用户友好的字符串,以识别镜头正在读取的字段?我想避免明确提供字段名称

    val md5LensAndName = (GenLens[Message](_.md5), "md5")

如果有一种解决方案也适用于具有多个组件的镜头,那么更好。对我来说,即使解决方案只能达到一个深度,也会很好。

1 个答案:

答案 0 :(得分:1)

这根本不可能。从概念上讲,镜头只不过是一对函数:一个用于从对象获取值,另一个用于使用给定值获取新对象。该功能可以通过访问源对象的字段来实现。实际上,即使GenLens宏也可以使用像_.field1.field2这样的链字段访问器来生成嵌套对象字段的复合透镜。起初这可能令人困惑,但这个功能有其用途。例如,您可以解耦数据存储和表示的格式:

import monocle._

case class Person private(value: String) {

  import Person._

  private def replace(
    array: Array[String], index: Int, item: String
  ): Array[String] = {
    val copy = Array.ofDim[String](array.length)
    array.copyToArray(copy)
    copy(index) = item
    copy
  }

  def replaceItem(index: Int, item: String): Person = {
    val array = value.split(delimiter)
    val newArray = replace(array, index, item)
    val newValue = newArray.mkString(delimiter)
    Person(newValue)
  }

  def getItem(index: Int): String = {
    val array = value.split(delimiter)
    array(index)
  }
}

object Person {

  private val delimiter: String = ";"

  val nameIndex: Int = 0

  val cityIndex: Int = 1

  def apply(name: String, address: String): Person =
    Person(Array(name, address).mkString(delimiter))
}

val name: Lens[Person, String] =
  Lens[Person, String](
    _.getItem(Person.nameIndex)
  )(
    name => person => person.replaceItem(Person.nameIndex, name)
  )

val city: Lens[Person, String] =
  Lens[Person, String](
    _.getItem(Person.cityIndex)
  )(
    city => person => person.replaceItem(Person.cityIndex, city)
  )

val person = Person("John", "London")
val personAfterMove = city.set("New York")(person)
println(name.get(personAfterMove)) // John
println(city.get(personAfterMove)) // New York

虽然性能不高,但该示例说明了这一想法:Person类没有cityaddress字段,但是通过将数据提取器和字符串重建功能包装到Lens,我们可以假装拥有它们。对于更复杂的物体,镜头组合照常工作:内部镜头只对提取的物体进行操作,依靠外部镜头将其打包回来。