Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generalized copy for optics #2777

Merged
merged 4 commits into from Aug 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions 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
Expand Down
@@ -0,0 +1,71 @@
package arrow.optics

public interface Copy<A> {
/**
* Changes the value of the element(s) pointed by the [Setter].
*/
public infix fun <B> Setter<A, B>.set(b: B)

/**
* Transforms the value of the element(s) pointed by the [Traversal].
*/
public infix fun <B> Traversal<A, B>.transform(f: (B) -> B)

/**
* Declares a block in which all optics are nested within
* the given [field]. Instead of:
*
* ```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we turn these into Knit examples?
Maybe we should discuss a documentation strategy for Arrow 2.0 in the weekly meeting? 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, it would be nice to have Knit examples working instead of simple blocks.

* 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 <B> inside(field: Traversal<A, B>, f: Copy<B>.() -> Unit): Unit =
field.transform { it.copy(f) }
}

// mutable builder of copies
private class CopyImpl<A>(var current: A): Copy<A> {
override fun <B> Setter<A, B>.set(b: B) {
current = this.set(current, b)
}
override fun <B> Traversal<A, B>.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> A.copy(f: Copy<A>.() -> Unit): A =
CopyImpl(this).also(f).current