Although we strongly recommend generating optics using the DSL and @optics
attribute, sometimes this is not possible. For those scenarios we provide a small utility package arrow-optics-reflect
which bridges Arrow Optics with Kotlin’s reflection capabilities.
Kotlin provides a simple way to obtain a reference to a member of a class, by using ClassName::memberName
. For example, given the following class definition:
data class Person(val name: String, val friends: List<String>)
we can use Person::name
and Person::friends
to refer to each of the fields in the class. Those references are very similar to optics.
In fact, what arrow-optics-reflect
does is provide extension methods which turn those references into optics. You can obtain a lens for the name
field in Person
by writing:
Person::name.lens
which you can later use as any other lens:
val p = Person("me", listOf("pat", "mat"))
val m = Person::name.lens.modify(p) { it.capitalize() }
⚠️ WARNING: this only works on data
classes with a public copy
method (which is the default.) Remember that, as opposed to a mutable variable, optics will always create a new copy when asking for modification.
Sometimes it’s preferable to expose a field using a different optic:
optional
to obtain an optional instead of a lens.iter
to obtain read-only access to it (technically, you obtain a fold.) If the type is a subclass of List
, you can use every
to get read/write access.val p = Person("me", listOf("pat", "mat"))
val m = Person::friends.every.modify(p) { it.capitalize() }
A common pattern in Kotlin programming is to define a sealed abstract class (or interface) with subclasses representing choices in a union.
sealed interface Cutlery
object Fork: Cutlery
object Spoon: Cutlery
We provide an instance
method which creates a prism which focus only on a certain subclass of a parent class. Both ends are important and must be provided when creating the optic:
instance<Cutlery, Fork>()
You can compose this optic freely with others. Here’s an example in which we obtain the number of forks in a list of cutlery using optics:
val things = listOf(Fork, Spoon, Fork)
val forks = Every.list<Cutlery>() compose instance<Cutlery, Fork>()
val noOfForks = forks.size(things)
Do you like Arrow?
✖