Изменение нескольких атрибутов класса данных с помощью объектива - PullRequest
0 голосов
/ 25 сентября 2019

Я экспериментирую с Линзами в Котлине, и мне было интересно, есть ли элегантный способ изменить несколько атрибутов одновременно для одного объекта.Допустим, мой домен выглядит примерно так:

@optics
data class Parameters(
    val duration: Int,
    val length: Int) {
  companion object
}

@optics
data class Calculation(
    val product: String
    val parameters: Parameters) {
  companion object
}

благодаря аннотациям @optics редактировать отдельные поля легко:

val calculation = Calculation(product = "prod", Parameters(duration = 10, length = 15))

Calculation.product.modify(calculation) { selectedProduct }
Calculation.parameters.duration(calculation) { newDuration() }
Calculation.parameters.length(calculation) { 10 }

Эти линзы прекрасно работают в изоляции,но какой правильный шаблон использовать, когда я хочу применить три преобразования одновременно?Я могу использовать var и просто перезаписывать calculation каждый раз, но это не кажется мне идиоматичным.

1 Ответ

2 голосов
/ 27 сентября 2019

Стрелка в настоящее время не предоставляет такой функциональности, но вы можете легко написать общее решение самостоятельно.

Фрагмент ниже демонстрирует, как его можно достичь, вы можете добавить дополнительные методы для компоновки от Lens<S, Tuple2<FA, FB>> до Lens<S, Tuple3<FA, FB, FC>> и т. д.

@optics data class Char(val name: String, val health: Int) {
  companion object
}

infix fun <S, FA, FB> Lens<S, FA>.aside(other: Lens<S, FB>): Lens<S, Tuple2<FA, FB>> = object : Lens<S, Tuple2<FA, FB>> {
  override fun get(s: S): Tuple2<FA, FB> = Tuple2(this@aside.get(s), other.get(s))
  override fun set(s: S, b: Tuple2<FA, FB>): S = other.set(this@aside.set(s, b.a), b.b)
}

fun main() {
  val original = Char("", 0)
  val charName: Lens<Char, String> = Char.name
  val charHealth: Lens<Char, Int> = Char.health
  val charNameAndHealth: Lens<Char, Tuple2<String, Int>> = charName.aside(charHealth)

  charNameAndHealth.modify(original) { Tuple2("Test", 30) }
}
...