Skip to content

Commit

Permalink
Various improvements to optics (#3421)
Browse files Browse the repository at this point in the history
  • Loading branch information
serras authored May 15, 2024
1 parent 88c722b commit 30cbd15
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ data class Focus(
val refinedType: KSType?,
val onlyOneSealedSubclass: Boolean = false,
val subclasses: List<String> = emptyList(),
val classNameWithParameters: String? = className,
) {
val refinedArguments: List<String>
get() = refinedType?.arguments?.filter {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ private fun processLensSyntax(ele: ADT, foci: List<Focus>, className: String): S
return if (ele.typeParameters.isEmpty()) {
foci.joinToString(separator = "\n") { focus ->
"""
|${ele.visibilityModifierName} inline val <S> $Lens<S, ${ele.sourceClassName}>.${focus.lensParamName()}: $Lens<S, ${focus.className}> inline get() = this + $className.${focus.lensParamName()}
|${ele.visibilityModifierName} inline val <S> $Optional<S, ${ele.sourceClassName}>.${focus.lensParamName()}: $Optional<S, ${focus.className}> inline get() = this + $className.${focus.lensParamName()}
|${ele.visibilityModifierName} inline val <S> $Traversal<S, ${ele.sourceClassName}>.${focus.lensParamName()}: $Traversal<S, ${focus.className}> inline get() = this + $className.${focus.lensParamName()}
|${ele.visibilityModifierName} inline val <__S> $Lens<__S, ${ele.sourceClassName}>.${focus.lensParamName()}: $Lens<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.lensParamName()}
|${ele.visibilityModifierName} inline val <__S> $Optional<__S, ${ele.sourceClassName}>.${focus.lensParamName()}: $Optional<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.lensParamName()}
|${ele.visibilityModifierName} inline val <__S> $Traversal<__S, ${ele.sourceClassName}>.${focus.lensParamName()}: $Traversal<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.lensParamName()}
|
""".trimMargin()
}
Expand All @@ -47,9 +47,9 @@ private fun processLensSyntax(ele: ADT, foci: List<Focus>, className: String): S
val joinedTypeParams = ele.typeParameters.joinToString(separator = ",")
foci.joinToString(separator = "\n") { focus ->
"""
|${ele.visibilityModifierName} inline fun <S,$joinedTypeParams> $Lens<S, $sourceClassNameWithParams>.${focus.lensParamName()}(): $Lens<S, ${focus.className}> = this + $className.${focus.lensParamName()}()
|${ele.visibilityModifierName} inline fun <S,$joinedTypeParams> $Optional<S, $sourceClassNameWithParams>.${focus.lensParamName()}(): $Optional<S, ${focus.className}> = this + $className.${focus.lensParamName()}()
|${ele.visibilityModifierName} inline fun <S,$joinedTypeParams> $Traversal<S, $sourceClassNameWithParams>.${focus.lensParamName()}(): $Traversal<S, ${focus.className}> = this + $className.${focus.lensParamName()}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Lens<__S, $sourceClassNameWithParams>.${focus.lensParamName()}(): $Lens<__S, ${focus.classNameWithParameters}> = this + $className.${focus.lensParamName()}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Optional<__S, $sourceClassNameWithParams>.${focus.lensParamName()}(): $Optional<__S, ${focus.classNameWithParameters}> = this + $className.${focus.lensParamName()}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Traversal<__S, $sourceClassNameWithParams>.${focus.lensParamName()}(): $Traversal<__S, ${focus.classNameWithParameters}> = this + $className.${focus.lensParamName()}()
|
""".trimMargin()
}
Expand All @@ -60,9 +60,9 @@ private fun processPrismSyntax(ele: ADT, dsl: SealedClassDsl, className: String)
if (ele.typeParameters.isEmpty()) {
dsl.foci.joinToString(separator = "\n\n") { focus ->
"""
|${ele.visibilityModifierName} inline val <S> $Optional<S, ${ele.sourceClassName}>.${focus.paramName}: $Optional<S, ${focus.className}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <S> $Prism<S, ${ele.sourceClassName}>.${focus.paramName}: $Prism<S, ${focus.className}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <S> $Traversal<S, ${ele.sourceClassName}>.${focus.paramName}: $Traversal<S, ${focus.className}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <__S> $Optional<__S, ${ele.sourceClassName}>.${focus.paramName}: $Optional<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <__S> $Prism<__S, ${ele.sourceClassName}>.${focus.paramName}: $Prism<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <__S> $Traversal<__S, ${ele.sourceClassName}>.${focus.paramName}: $Traversal<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.paramName}
|
""".trimMargin()
}
Expand All @@ -74,9 +74,9 @@ private fun processPrismSyntax(ele: ADT, dsl: SealedClassDsl, className: String)
else -> focus.refinedArguments.joinToString(separator = ",")
}
"""
|${ele.visibilityModifierName} inline fun <S,$joinedTypeParams> $Optional<S, $sourceClassNameWithParams>.${focus.paramName}(): $Optional<S, ${focus.className}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <S,$joinedTypeParams> $Prism<S, $sourceClassNameWithParams>.${focus.paramName}(): $Prism<S, ${focus.className}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <S,$joinedTypeParams> $Traversal<S, $sourceClassNameWithParams>.${focus.paramName}(): $Traversal<S, ${focus.className}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Optional<__S, $sourceClassNameWithParams>.${focus.paramName}(): $Optional<__S, ${focus.classNameWithParameters}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Prism<__S, $sourceClassNameWithParams>.${focus.paramName}(): $Prism<__S, ${focus.classNameWithParameters}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Traversal<__S, $sourceClassNameWithParams>.${focus.paramName}(): $Traversal<__S, ${focus.classNameWithParameters}> = this + $className.${focus.paramName}()
|
""".trimMargin()
}
Expand All @@ -86,11 +86,11 @@ private fun processIsoSyntax(ele: ADT, dsl: ValueClassDsl, className: String): S
if (ele.typeParameters.isEmpty()) {
dsl.foci.joinToString(separator = "\n\n") { focus ->
"""
|${ele.visibilityModifierName} inline val <S> $Iso<S, ${ele.sourceClassName}>.${focus.paramName}: $Iso<S, ${focus.className}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <S> $Lens<S, ${ele.sourceClassName}>.${focus.paramName}: $Lens<S, ${focus.className}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <S> $Optional<S, ${ele.sourceClassName}>.${focus.paramName}: $Optional<S, ${focus.className}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <S> $Prism<S, ${ele.sourceClassName}>.${focus.paramName}: $Prism<S, ${focus.className}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <S> $Traversal<S, ${ele.sourceClassName}>.${focus.paramName}: $Traversal<S, ${focus.className}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <__S> $Iso<__S, ${ele.sourceClassName}>.${focus.paramName}: $Iso<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <__S> $Lens<__S, ${ele.sourceClassName}>.${focus.paramName}: $Lens<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <__S> $Optional<__S, ${ele.sourceClassName}>.${focus.paramName}: $Optional<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <__S> $Prism<__S, ${ele.sourceClassName}>.${focus.paramName}: $Prism<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.paramName}
|${ele.visibilityModifierName} inline val <__S> $Traversal<__S, ${ele.sourceClassName}>.${focus.paramName}: $Traversal<__S, ${focus.classNameWithParameters}> inline get() = this + $className.${focus.paramName}
|
""".trimMargin()
}
Expand All @@ -102,11 +102,11 @@ private fun processIsoSyntax(ele: ADT, dsl: ValueClassDsl, className: String): S
else -> focus.refinedArguments.joinToString(separator = ",")
}
"""
|${ele.visibilityModifierName} inline fun <S,$joinedTypeParams> $Iso<S, $sourceClassNameWithParams>.${focus.paramName}(): $Iso<S, ${focus.className}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <S,$joinedTypeParams> $Lens<S, $sourceClassNameWithParams>.${focus.paramName}(): $Lens<S, ${focus.className}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <S,$joinedTypeParams> $Optional<S, $sourceClassNameWithParams>.${focus.paramName}(): $Optional<S, ${focus.className}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <S,$joinedTypeParams> $Prism<S, $sourceClassNameWithParams>.${focus.paramName}(): $Prism<S, ${focus.className}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <S,$joinedTypeParams> $Traversal<S, $sourceClassNameWithParams>.${focus.paramName}(): $Traversal<S, ${focus.className}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Iso<__S, $sourceClassNameWithParams>.${focus.paramName}(): $Iso<__S, ${focus.classNameWithParameters}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Lens<__S, $sourceClassNameWithParams>.${focus.paramName}(): $Lens<__S, ${focus.classNameWithParameters}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Optional<__S, $sourceClassNameWithParams>.${focus.paramName}(): $Optional<__S, ${focus.classNameWithParameters}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Prism<__S, $sourceClassNameWithParams>.${focus.paramName}(): $Prism<__S, ${focus.classNameWithParameters}> = this + $className.${focus.paramName}()
|${ele.visibilityModifierName} inline fun <__S,$joinedTypeParams> $Traversal<__S, $sourceClassNameWithParams>.${focus.paramName}(): $Traversal<__S, ${focus.classNameWithParameters}> = this + $className.${focus.paramName}()
|
""".trimMargin()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,30 +21,10 @@ private fun OpticsProcessorOptions.processElement(ele: ADT, foci: List<Focus>):
}
val firstLine = when {
ele.typeParameters.isEmpty() ->
"${ele.visibilityModifierName} $inlineText val ${ele.sourceClassName}.Companion.${focus.paramName}: $Prism<${ele.sourceClassName}, ${focus.className}> $inlineText get()"
"${ele.visibilityModifierName} $inlineText val ${ele.sourceClassName}.Companion.${focus.paramName}: $Prism<${ele.sourceClassName}, ${focus.classNameWithParameters}> $inlineText get()"
else ->
"${ele.visibilityModifierName} $inlineText fun $angledTypeParameters ${ele.sourceClassName}.Companion.${focus.paramName}(): $Prism<$sourceClassNameWithParams, ${focus.className}>"
"${ele.visibilityModifierName} $inlineText fun $angledTypeParameters ${ele.sourceClassName}.Companion.${focus.paramName}(): $Prism<$sourceClassNameWithParams, ${focus.classNameWithParameters}>"
}

val elseBranch = if (focus.onlyOneSealedSubclass) {
""
} else {
"""
| else -> ${ele.sourceName}.left()
""".trimMargin()
}

"""
|$firstLine = $Prism(
| getOrModify = { ${ele.sourceName}: $sourceClassNameWithParams ->
| when (${ele.sourceName}) {
| is ${focus.className} -> ${ele.sourceName}.right()
| $elseBranch
| }
| },
| reverseGet = ::identity
|)
|
""".trimMargin()
"$firstLine = $Prism.pInstanceOf()"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,13 @@ internal fun evalAnnotatedPrismElement(
when {
element.isSealed -> {
val sealedSubclasses = element.getSealedSubclasses().toList()
sealedSubclasses.map {
sealedSubclasses.map { subclass ->
Focus(
it.primaryConstructor?.returnType?.resolve()?.qualifiedString() ?: it.qualifiedNameOrSimpleName,
it.simpleName.asString().replaceFirstChar { c -> c.lowercase(Locale.getDefault()) },
it.superTypes.first().resolve(),
subclass.qualifiedNameOrSimpleName,
subclass.simpleName.asString().replaceFirstChar { c -> c.lowercase(Locale.getDefault()) },
subclass.superTypes.first().resolve(),
onlyOneSealedSubclass = sealedSubclasses.size == 1,
classNameWithParameters = subclass.qualifiedNameOrSimpleNameWithTypeParameters,
)
}
}
Expand All @@ -90,6 +91,12 @@ internal fun evalAnnotatedPrismElement(
internal val KSDeclaration.qualifiedNameOrSimpleName: String
get() = (qualifiedName ?: simpleName).asSanitizedString()

internal val KSClassDeclaration.qualifiedNameOrSimpleNameWithTypeParameters: String
get() = when {
typeParameters.isEmpty() -> qualifiedNameOrSimpleName
else -> "$qualifiedNameOrSimpleName<${typeParameters.joinToString { it.simpleName.asString() }}>"
}

internal fun evalAnnotatedDataClass(
element: KSClassDeclaration,
errorMessage: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,5 +130,49 @@ class DSLTests {
""".compilationSucceeds()
}

@Test
fun `Using S as a type, #3399`() {
"""
|$`package`
|$imports
|@optics
|data class Box<S>(val s: S) {
| companion object
|}
|
|val i: Lens<Box<Int>, Int> = Box.s()
|val r = i != null
""".evals("r" to true)
}

@Test
fun `Nested generic sealed hierarchies, #3384`() {
"""
|$`package`
|$imports
|@optics
|sealed interface LoadingContentOrError<out Data> {
| data object Loading : LoadingContentOrError<Nothing>
|
| @optics
| sealed interface ContentOrError<out Data> : LoadingContentOrError<Data> {
| companion object
| }
|
| @optics
| data class Content<out Data>(val data: Data) : ContentOrError<Data> {
| companion object
| }
|
| @optics
| data class Error(val error: Throwable) : ContentOrError<Nothing> {
| companion object
| }
|
| companion object
|}
""".compilationSucceeds()
}

// Db.content.at(At.map(), One).set(db, None)
}
6 changes: 6 additions & 0 deletions arrow-libs/optics/arrow-optics/api/arrow-optics.api
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public final class arrow/optics/Every {
public static final fun tuple9 ()Larrow/optics/PTraversal;
}

public final class arrow/optics/LensKt {
public static final fun composeNull (Larrow/optics/PLens;Larrow/optics/PLens;)Larrow/optics/PLens;
}

public final class arrow/optics/ListKt {
public static final fun cons (Ljava/lang/Object;Ljava/util/List;)Ljava/util/List;
public static final fun get (Larrow/optics/PLens;I)Larrow/optics/POptional;
Expand Down Expand Up @@ -284,6 +288,7 @@ public abstract interface class arrow/optics/PPrism : arrow/optics/POptional {
public abstract fun liftNullable (Lkotlin/jvm/functions/Function1;)Lkotlin/jvm/functions/Function1;
public abstract fun modify (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static fun none ()Larrow/optics/PPrism;
public static fun pInstanceOf (Lkotlin/reflect/KClass;)Larrow/optics/PPrism;
public static fun pLeft ()Larrow/optics/PPrism;
public static fun pRight ()Larrow/optics/PPrism;
public static fun pSome ()Larrow/optics/PPrism;
Expand All @@ -303,6 +308,7 @@ public final class arrow/optics/PPrism$Companion {
public final fun none ()Larrow/optics/PPrism;
public final fun only (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Larrow/optics/PPrism;
public static synthetic fun only$default (Larrow/optics/PPrism$Companion;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Larrow/optics/PPrism;
public final fun pInstanceOf (Lkotlin/reflect/KClass;)Larrow/optics/PPrism;
public final fun pLeft ()Larrow/optics/PPrism;
public final fun pRight ()Larrow/optics/PPrism;
public final fun pSome ()Larrow/optics/PPrism;
Expand Down
Loading

0 comments on commit 30cbd15

Please sign in to comment.