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 tomodify
byfilter
and then increase thename
, but not toage
byfilter
and 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.