diff --git a/arrow-libs/optics/arrow-optics/api/arrow-optics.api b/arrow-libs/optics/arrow-optics/api/arrow-optics.api index d8ef229e8fe..aa6ac451a63 100644 --- a/arrow-libs/optics/arrow-optics/api/arrow-optics.api +++ b/arrow-libs/optics/arrow-optics/api/arrow-optics.api @@ -1,3 +1,17 @@ +public abstract interface class arrow/optics/Copy { + public abstract fun inside (Larrow/optics/PTraversal;Lkotlin/jvm/functions/Function1;)V + public abstract fun set (Larrow/optics/PSetter;Ljava/lang/Object;)V + public abstract fun transform (Larrow/optics/PTraversal;Lkotlin/jvm/functions/Function1;)V +} + +public final class arrow/optics/Copy$DefaultImpls { + public static fun inside (Larrow/optics/Copy;Larrow/optics/PTraversal;Lkotlin/jvm/functions/Function1;)V +} + +public final class arrow/optics/CopyKt { + public static final fun copy (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; +} + public abstract interface class arrow/optics/Fold { public static final field Companion Larrow/optics/Fold$Companion; public abstract fun all (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Z diff --git a/arrow-libs/optics/arrow-optics/src/commonMain/kotlin/arrow/optics/Copy.kt b/arrow-libs/optics/arrow-optics/src/commonMain/kotlin/arrow/optics/Copy.kt new file mode 100644 index 00000000000..5b231df3623 --- /dev/null +++ b/arrow-libs/optics/arrow-optics/src/commonMain/kotlin/arrow/optics/Copy.kt @@ -0,0 +1,71 @@ +package arrow.optics + +public interface Copy { + /** + * Changes the value of the element(s) pointed by the [Setter]. + */ + public infix fun Setter.set(b: B) + + /** + * Transforms the value of the element(s) pointed by the [Traversal]. + */ + public infix fun Traversal.transform(f: (B) -> B) + + /** + * Declares a block in which all optics are nested within + * the given [field]. Instead of: + * + * ``` + * x.copy { + * X.a.this set "A" + * X.a.that set "B" + * } + * ``` + * + * you can write: + * + * ``` + * x.copy { + * inside(X.a) { + * A.this set "A" + * A.that set "B" + * } + * } + * ``` + */ + public fun inside(field: Traversal, f: Copy.() -> Unit): Unit = + field.transform { it.copy(f) } +} + +// mutable builder of copies +private class CopyImpl(var current: A): Copy { + override fun Setter.set(b: B) { + current = this.set(current, b) + } + override fun Traversal.transform(f: (B) -> B) { + current = this.modify(current, f) + } +} + +/** + * Small DSL which parallel Kotlin's built-in `copy`, + * but using optics instead of field names. See [Copy] + * for the operations allowed inside the block. + * + * This allows declaring changes on nested elements, + * preventing the "nested `copy` problem". Instead of: + * + * ``` + * person.copy(address = person.address.copy(city = "Madrid")) + * ``` + * + * you can write: + * + * ``` + * person.copy { + * Person.address.city set "Madrid" + * } + * ``` + */ +public fun A.copy(f: Copy.() -> Unit): A = + CopyImpl(this).also(f).current