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

Enable builder inference for optics copy #2792

Merged
merged 4 commits into from Aug 9, 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
3 changes: 3 additions & 0 deletions arrow-libs/optics/arrow-optics/api/arrow-optics.api
Expand Up @@ -151,6 +151,9 @@ public final class arrow/optics/ListKt {
public static final fun unsnoc (Ljava/util/List;)Lkotlin/Pair;
}

public abstract interface annotation class arrow/optics/OpticsCopyMarker : java/lang/annotation/Annotation {
}

public final class arrow/optics/OptionalGetterKt {
public static final fun OptionalGetter (Lkotlin/jvm/functions/Function1;)Larrow/optics/POptionalGetter;
}
Expand Down
@@ -1,5 +1,11 @@
package arrow.optics

import kotlin.experimental.ExperimentalTypeInference

@DslMarker
public annotation class OpticsCopyMarker

@OpticsCopyMarker
Copy link
Member

Choose a reason for hiding this comment

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

What does this do on the interface? I thought it was supposed to go on the functions, the documentation didn’t clarify it for me 😅
Since it’s applicable to interface, I’m wondering what it does.

Is it applied to all DSL functions that have this as a lambda receiver?

Copy link
Member Author

Choose a reason for hiding this comment

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

As I understand from https://kotlinlang.org/docs/type-safe-builders.html#scope-control-dslmarker, you put the annotation in the interface which will be in scope in the builder, which in this case is Copy.

Copy link
Member Author

Choose a reason for hiding this comment

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

As I understand the @DslMarker is the thing that marks the member of that interface as "keywords", whereas @BuilderInference changes how type checking works to better suit this kind of pattern.

Copy link
Member

Choose a reason for hiding this comment

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

Okay, I asked because I used @DslMarker differently in the Arrow 2.0 branch and it worked for me in IDEA 🤔

Copy link
Member Author

Choose a reason for hiding this comment

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

According to the docs (emphasis mine):

To make the compiler start controlling scopes you only have to annotate the types of all receivers used in the DSL with the same marker annotation. For instance, for HTML Builders you declare an annotation @HTMLTagMarker:

@DslMarker
annotation class HtmlTagMarker

An annotation class is called a DSL marker if it is annotated with the @DslMarker annotation.

[...]

After you've added this annotation, the Kotlin compiler knows which implicit receivers are part of the same DSL and allows to call members of the nearest receivers only.

public interface Copy<A> {
/**
* Changes the value of the element(s) pointed by the [Setter].
Expand Down Expand Up @@ -33,7 +39,8 @@ public interface Copy<A> {
* }
* ```
*/
public fun <B> inside(field: Traversal<A, B>, f: Copy<B>.() -> Unit): Unit =
@OptIn(ExperimentalTypeInference::class)
public fun <B> inside(field: Traversal<A, B>, @BuilderInference f: Copy<B>.() -> Unit): Unit =
field.transform { it.copy(f) }
}

Expand Down Expand Up @@ -67,5 +74,6 @@ private class CopyImpl<A>(var current: A): Copy<A> {
* }
* ```
*/
public fun <A> A.copy(f: Copy<A>.() -> Unit): A =
@OptIn(ExperimentalTypeInference::class)
public fun <A> A.copy(@BuilderInference f: Copy<A>.() -> Unit): A =
CopyImpl(this).also(f).current