Is there already a good approach or DSL for annota...
# ksp
m
Is there already a good approach or DSL for annotating foreign types? For example in a multi-module project: module “model” contains all model types module “persistence” handles database serialization and thus needs database-specific annotations on types provided by “model” module “serialization” handles JSON serialization and thus needs JSON-specific annotations on types provided by “model” For proper abstraction, the annotations must not be put on the model types directly because that’s an implementation detail. And may vary depending on the dependent module.
t
Not sure if I understand correctly, but that might be achieved with type alias or use-site annotations.
m
Basically I want to separate the type from the annotations. In the following example I have
@Json
annotations directly in the
data class
. The question is if there’s a type-safe way to annotate types, properties and methods in another module and leave the
data class
without annotations. But I guess that would need some language additions. Or a fancy mix of DSL + use-site annotations.
t
I guess it can be emulated by processors. For example,
Copy code
@AnnotatingForeignType
@Json(...)
typealias ExtStructure = Structure
Whenever the processor sees the definition in the above, it can process
Structure
as if it were annotated with Json.
m
For types that could work. It won’t work for any members of the type though.
z
I don’t think what you’re describing works without something like protocol extensions
and even then, you should just write your own composing type rather than try to process external sources. Would be a massive headache for incremental compilation, and opens a can of worms for people trying to automatically apply serialization to types they don’t own
m
Yeah, I’ll probably have to resort to a Kotlin DSL + ksp mix. So I use Kotlin DSL to describe something about an existing type. Then I use ksp to parse that and generate source files based on the DSL used. If ksp can do that 🙂
I’m interested in compile-time annotations only. I agree that the compiler would go nuts with binary or even runtime annotations due to multiple modules modifying the annotations of the same type.
z
KSP is intended as an annotation processor replacement, what you’re describing seems pretty borderline and you likely want to look at writing either a code gen tool that reads a defined spec format or a full compiler plugin. Seems like you’re trying to use a hammer for a screw
m
@Marc Knaup having strict and clear architecture you recognize and process with compile extension for your foreign classes by e.g package name ?
m
@Michal Harakal I don’t think I understand 🤔
m
Or me 🙂 you need a rule to mark a class to be processed somehow during compilation, right? It can be explicit one as annotation or implicit (package name). Just as an idea.
m
Not just a class, but also members of that class. Basically augmenting it to add functionality in a modular way. Let’s say I have a
data class Duck(…)
. Now in a different module I want to provide info about how that class is supposed to be serialized/deserialized to/from JSON. In another module I want to provide info about how that class is supposed to represented in GraphQL. And so on. That info is needed at compile time so that code generation can create the code needed for serialization, GraphQL definitions instances, etc. Annotations are great for something like that. But they’re limited to within a single module. That means I suddenly have mix my data model (my definition of Duck) with completely unrelated information (JSON, GraphQL). The latter are an implementation detail how I use the model in various cases. So these implementation details should stay away from the data model. Which likely means that annotations are just not the right fit for that.
m
In that case you probably want have a mapper. There is a chance, that your datetime property will be as a Kontlix.date in your domain, but as an ISO date string for JSON and long timestamp in graphgl. And indeed, having this as mix in the data class break for sure some of the SOLID rules. But I’ve got also tired to manually write all that mappers, which reminds me of https://mapstruct.org/ At now having the power of kotlin compiler extentions and multiplatform … mhmmm..
m
Exactly. That’s why I’d like to have some DSL to just give the minimal info necessary for a compiler plugin to generate all these mappings and definitions. Annotations can be one kind of DSL but they’re very limited. You have to put them directly on the definitions. That’s why I likely can’t use annotations and have to look for alternatives. Here’s a simple GraphQL example with custom DSL. Still evaluated at runtime using reflection. And the repetitive
User::
is annoying. I could do the same with annotations, but not in a different module. And there are more complex cases, like when I define a field inline,
m
I remember to see something similar in one of videos from Lealand (jetpack compose leader). They also need a render instance in every composable method as a parameter and they inject it with compiler plugin … http://www.twitch.tv/intelligibabble/v/750949224
Watching Roman ELizarow's talk and it looks like @*decorators* seems to be a help without compiler plugin
m
Yes, thought the same 😄
I wonder how it related to annotations.
m
my understanig is to add more features to meta programming capabailities to stanhdard kotlin
annotoation are for my always an external step in compilation process
m
Yeah but they’d share the same syntax, yet be applied completely differently.
m
yeah, right, lets see how it works in real. I would like definitely digg deeper into conpiler plufgins anyway, but having nice features build in make is cool too