如何将单片眼镜打印为 属性 访问器样式字符串
How to print a Monocle Lens as a property accessor style string
使用 Monocle 我可以定义一个镜头来阅读案例 class 成员没有问题,
val md5Lens = GenLens[Message](_.md5)
这可用于比较两个对象之间 md5
的值,当值不同时会失败并显示包含字段名称的错误消息。
有没有一种方法可以仅从 Lens
生成一个用户友好的字符串来标识镜头正在读取的字段?我想避免显式提供字段名称
val md5LensAndName = (GenLens[Message](_.md5), "md5")
如果有一种解决方案也适用于具有多个组件的镜头,那就更好了。对我来说,即使该解决方案仅适用于一个深度也是好的。
这根本不可能。从概念上讲,lens 无非是一对函数:一个从 object 获取一个值,一个使用给定的值获取新的 object。该功能可以通过访问或不访问源对象的字段来实现。事实上,甚至 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
class 没有 city
或 address
字段,但是通过包装数据提取器和字符串重建函数到 Lens
,我们可以假装它有它们。对于更复杂的物体,镜头组合照常工作:内镜头只对提取的物体进行操作,依靠外镜头将其打包回去。
使用 Monocle 我可以定义一个镜头来阅读案例 class 成员没有问题,
val md5Lens = GenLens[Message](_.md5)
这可用于比较两个对象之间 md5
的值,当值不同时会失败并显示包含字段名称的错误消息。
有没有一种方法可以仅从 Lens
生成一个用户友好的字符串来标识镜头正在读取的字段?我想避免显式提供字段名称
val md5LensAndName = (GenLens[Message](_.md5), "md5")
如果有一种解决方案也适用于具有多个组件的镜头,那就更好了。对我来说,即使该解决方案仅适用于一个深度也是好的。
这根本不可能。从概念上讲,lens 无非是一对函数:一个从 object 获取一个值,一个使用给定的值获取新的 object。该功能可以通过访问或不访问源对象的字段来实现。事实上,甚至 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
class 没有 city
或 address
字段,但是通过包装数据提取器和字符串重建函数到 Lens
,我们可以假装它有它们。对于更复杂的物体,镜头组合照常工作:内镜头只对提取的物体进行操作,依靠外镜头将其打包回去。