這是根本不可能的。從概念上講,鏡頭只不過是一對功能:一個是從對象獲取一個值,另一個是使用給定值獲取新對象。該功能可以通過訪問源對象字段的方式來實現。實際上,即使是GenLens
宏也可以使用像3210這樣的鏈式場訪問器來爲嵌套對象的字段生成複合透鏡。起初可能會讓人困惑,但這個功能有其用處。例如,您可以斷開數據存儲和代表性的格式:
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
類沒有city
或address
領域,但通過包裝數據提取和我們可以假裝它有它們。對於更復雜的物體,透鏡組成如常工作:內部透鏡僅對提取物體進行操作,依靠外部透鏡將其折回。
即使您有一個字段鏈需要處理,對於類似LabellessGeneric的字段來標記'HList'字段,在字段中使用標籤的概念並不是概念上不可能的。如果不修改Lens的隱式宏,或者按照'LabelledGeneric'的精神實現'LabelledLens',就不可能實現。 – flavian
我可以通過使用基於屬性訪問器的DSL的https://github.com/kenbot/goggles來解決我的問題。 –