-
Notifications
You must be signed in to change notification settings - Fork 29
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
Introduce automatic Generation of Delegating Lenses: Improves Validation Support for Sealed Class Hierarchies #876
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left some minor comments about formatting nd similar stuff.
Another general point: Should we adjust the lens documentation and add a paragraph about sealed class/interface specific lenses (such as up- and down-casting lenses and what to use)?
lenses-annotation-processor/src/jvmMain/kotlin/dev/fritz2/lens/LensesProcessor.kt
Outdated
Show resolved
Hide resolved
lenses-annotation-processor/src/jvmMain/kotlin/dev/fritz2/lens/LensesProcessor.kt
Outdated
Show resolved
Hide resolved
lenses-annotation-processor/src/jvmMain/kotlin/dev/fritz2/lens/LensesProcessor.kt
Outdated
Show resolved
Hide resolved
lenses-annotation-processor/src/jvmMain/kotlin/dev/fritz2/lens/LensesProcessor.kt
Outdated
Show resolved
Hide resolved
lenses-annotation-processor/src/jvmMain/kotlin/dev/fritz2/lens/LensesProcessor.kt
Outdated
Show resolved
Hide resolved
lenses-annotation-processor/src/jvmMain/kotlin/dev/fritz2/lens/LensesProcessor.kt
Outdated
Show resolved
Hide resolved
lenses-annotation-processor/src/jvmMain/kotlin/dev/fritz2/lens/LensesProcessor.kt
Show resolved
Hide resolved
lenses-annotation-processor/src/jvmMain/kotlin/dev/fritz2/lens/LensesProcessor.kt
Outdated
Show resolved
Hide resolved
Regarding new section(s) in the documentation we should rework the following phrase from the section lenses-in-depth:
to
This way we do not disturb the readers flow. We could add some info-box at the end of the section that forwards to the dedicated section(s) |
- add support for lenses generation for sealed classes and interfaces - automatic lenses generation generates casting lenses for up- and down-casting. Those are important building blocks for integrating central validation code in sealed base classes into the overall validation process. - improves support for dealing with a sealed base class as central model for an application store (up-casting lenses are needed for this) solves #874
03d495f
to
46638b7
Compare
The newly introduced support for lenses generation for sealed classes or interfaces (see #876) must allow to configure whether some property should be picked for lens generation or not. This heavily depends on the implementation of those. The default case is to pick a property, but sometimes a property will not be implemented as constructor property inside the child data class. For such cases it is now possible to mark such properties with the `@NoLens` annotation inside the sealed type. Such marked properties will get ignored by the lens generator, so no delegating lens will be created. Beware that this annotation is not evaluated inside the constructor of data classes! Imagine the following example to see `@NoLens` in action: ```kotlin @lenses sealed class Framework { // Ignore this property for delegating lens generation. // The property is considered to be constant for all objects, // see data class below @nolens val ignore: String abstract val foo: String companion object } data class Fritz2 ( override val foo: String, ) : Framework { // not part of the "data", so not possible to change at copy! // Because of that, we cannot define any valid lens in the sealed base, // so we must mark it to exclude it for lens creation! override val ignore: String = "Fritz2" } ```
* Add new `@NoLens` annotation The newly introduced support for lenses generation for sealed classes or interfaces (see #876) must allow to configure whether some property should be picked for lens generation or not. This heavily depends on the implementation of those. The default case is to pick a property, but sometimes a property will not be implemented as constructor property inside the child data class. For such cases it is now possible to mark such properties with the `@NoLens` annotation inside the sealed type. Such marked properties will get ignored by the lens generator, so no delegating lens will be created. Beware that this annotation is not evaluated inside the constructor of data classes! Imagine the following example to see `@NoLens` in action: ```kotlin @lenses sealed class Framework { // Ignore this property for delegating lens generation. // The property is considered to be constant for all objects, // see data class below @nolens val ignore: String abstract val foo: String companion object } data class Fritz2 ( override val foo: String, ) : Framework { // not part of the "data", so not possible to change at copy! // Because of that, we cannot define any valid lens in the sealed base, // so we must mark it to exclude it for lens creation! override val ignore: String = "Fritz2" } ``` --------- Co-authored-by: christian.hausknecht <[email protected]> Co-authored-by: Lysander <[email protected]>
Motivation
Currently the support for validating sealed class hierachies is rather limited, as our provided mechanisms wont work as the following example shows:
We cannot access the generated Lenses from within the interface scope, as we cannot refer to the companion objects of the implementing classes.
We could create the needed lenses by hand of course:
We cannot really provide some good
setter
though!Warning
This is very dangerous, as anyone could use the
Lens
for store-mapping, which would fail then!making this lens
private
would minimize the risk of leaking outside, but a small risk remains of course...Solution
The solution is kinda simple: We have to implement the lens within the sealed base type, but delegate the "work" to the implementing types! This way we can use the polymorphic aspect in a safe way, as the
sealed
property guarantees alle types are known at compile time:Armed with a working
Lens
for the common properties, we could use those within the central validation code:There is one problem left: How can we call this common validation from within the validation code of one implementing child? The type of the
inspector
hast to beInspector<Product>
and notInspector<WebFramework>
orInspector<Pizza>
.Another
Lens
will help us there: It must cast the specific type to the base type, so its role is down-casting. That is why we refer to those kind of lenses with the term "down-casting-lens":Now we got all the building blocks to compose idiomatic fritz2 validation codes within a sealed class hierarchy.
Technical Solution
This PR adds the generation of delegating lenses and up- and down-casting-lenses to the
lenses-annotation-processor
of fritz2. So by now you can add@Lenses
annotations also tosealed
classes or interfaces in order to generate the delegating- and up-casting-lenses.(up-casting-lenses are useful for dealing with sealed base types as model-store-types. See issue #875 for more details)
The down-casting-lenses are always named by the base type's name, so
sealed class BaseType
would createbaseType()
lenses-factories in each child's companion objects.View the full example code aggreagated in one code snippet:
closes #874