sindrenm
01/28/2025, 9:51 AMIndex the idiomatic way of focusing down on a specific item in a list based on a criteria (e.g. “find by ID”), or is there something I haven't realized yet? We find ourselves needing to do this quite often, and we've resorted into essentially this pattern:
@optics data class Lesson(val id: String, val name: String) {
companion object
}
@optics data class Lessons(val items: List<Lesson>) {
companion object
}
fun main() {
val lessons = Lessons(listOf(Lesson("1", "Lesson One"), Lesson("2", "Lesson Two")))
Lessons.items.index(atLessonId, "1").name.set(lessons, "New Lesson One Name")
}
private val atLessonId = Index<List<Lesson>, String, Lesson> { id ->
Optional(
getOption = { lesson: List<Lesson> ->
lesson.firstOrNone { it.id == id }
},
set = { lessons: List<Lesson>, lesson: Lesson ->
TODO("Not implemented for brevity")
},
)
}Alejandro Serrano.Mena
01/28/2025, 9:57 AMfilter for that https://apidocs.arrow-kt.io/arrow-optics/arrow.optics.dsl/filter.htmlAlejandro Serrano.Mena
01/28/2025, 9:58 AMLessons.items.filter { it.id == id }.name.set(...)sindrenm
01/28/2025, 10:48 AMfilter, but I noticed it was part of a “delicate optics API”, so I got a little scared.
Also, I guess you'd need an every in there?
Lessons.items.every
.filter { it.id == "1" }
.name.set(lessons, "New Lesson One Name")sindrenm
01/28/2025, 10:48 AM@DelicateOptic annotation on it, though?sindrenm
01/28/2025, 10:50 AM⚠️ Warning: when usingwith this optic, the transformation should not alter the values that are taken into account by the predicate. For example, it is fine tomodifybyfilterand then increase thename, but not toagebyfilterand then capitalize thename.name
sindrenm
01/28/2025, 10:53 AMAlejandro Serrano.Mena
01/28/2025, 11:03 AMWhat's the reason for theexactly what you mention. I've been bitten a couple of times by this myself, so I think a remainder for "please read the docs and think twice" is good here.annotation on it, though?@DelicateOptic
sindrenm
01/28/2025, 12:39 PMfun main() {
val lessons = Lessons(listOf(Lesson("1", "Lesson One"), Lesson("2", "Lesson Two")))
val filterId1 = Lessons.items.every.filter { it.id == "1" }
// Overwriting the entire lesson
println(filterId1.set(lessons, Lesson("3", "Lesson One")))
// => Lessons(items=[Lesson(id=3, name=Lesson One), Lesson(id=2, name=Lesson Two)])
// Setting the filtered value using `set`
println(filterId1.id.set(lessons, "3"))
// => Lessons(items=[Lesson(id=3, name=Lesson One), Lesson(id=2, name=Lesson Two)])
// Setting the filtered value using `modify`
println(filterId1.id.modify(lessons) { "3" })
// => Lessons(items=[Lesson(id=3, name=Lesson One), Lesson(id=2, name=Lesson Two)])
}
Unless I'm misunderstanding the warning?Alejandro Serrano.Mena
01/28/2025, 12:43 PMsindrenm
01/28/2025, 12:49 PMset, modify or the like “terminal”? As in, they won't give you back a Lens, Optional, Traversal or anything Optics-specific?Alejandro Serrano.Mena
01/28/2025, 3:16 PMmodify or set in a rowsindrenm
01/29/2025, 8:35 AMvar lessons = Lessons(listOf(Lesson("1", "Lesson One"), Lesson("2", "Lesson Two")))
val filterId1 = Lessons.items.every.filter { it.id == "1" }
lessons = filterId1.id.set("3")
lessons = filterId1.id.set("4")
I can definitely see why that wouldn't work, but perhaps in more contrived setups it's less obvious.