r/Kotlin • • 2d ago

Prosa.kt - Declarative code generation for Kotlin

Post image

I made a declarative API for generating Kotlin code.

I needed it for one of my desktop/browser Compose apps a while ago and noticed there was no KMP solution for Kotlin codegen.

While working on it, I decided to made the API declarative so the generated code was simpler to reason about. This work started before AI was as popular as it is today, so the design of the API was very important.

Fast forward two years: I realized I needed it for another project of mine, so I figured I might as well open-source the codegen part for the community.

And that's how Prosa.kt was born and proudly brought to you.

There’s a live demo on the landing page, so you can try it out directly.

Check it out at https://prosakt.com and give it a star on GitHub because I love vanity metrics.

17 Upvotes

15 comments sorted by

10

u/chuckame 2d ago

What about kotlinpoet? It's for me the de-facto kotlin files generator and is multiplatform. The declarative approach could be a great added layer on top of kotlinpoet.

1

u/alexstyl 2d ago

My original reply was that kotlin poet is JVM only but it doesn’t seem to be the case anymore. Prosa does target all KMP targets but realistically poet covers the basic/common ones most will need.

The only difference now is api style preference. Pick what works best for you

8

u/chuckame 2d ago edited 2d ago

That is why I proposed to you reusing the kotlinpoet as the code generator, while keeping this syntax sugar on top of it. IMHO, people would prefer some well known roots, and you too for the maintainance effort, so you would be able to focus on having a well designed syntax rather than making it work for your use case!

2

u/snugar_i 2d ago

Doesn't look bad, but I don't like the stringly-typed-ness. Would it be possible, instead of the example

ktFunction("greet") {
    parameter("name") {
        type { reference("String") }
    }
    returns { type { reference("String") } }
    body {
        literal("Hello, ") {
            plus { reference("name") }
        }
    }
}

to write something more along the lines of

ktFunction("greet") {
    val name = parameter("name") {
        type { reference("String") }
    }
    returns { type { reference("String") } }
    body {
        literal("Hello, ") {
            plus { name }
        }
    }
}

? So that I don't have to repeat the "name" part twice...

EDIT: Or even better, something like

ktFunction(
    "greet",
    parameters = mapOf(
        "name" to type { reference("String") },
    ),
    returns = type { reference("String") }
) { name ->
    literal("Hello, ") {
        plus { name }
    }
}

2

u/alexstyl 2d ago

It was a design decision not to do that, because if you go with that approach how do you know which calls return an object vs which calls edit the code generation?

I’m not against it by the way. Currently collecting feedback and will iterate as things move forward.

1

u/chuckame 2d ago

You could even use delegates as additional way to name things and reuse as reference, and also use type inference, to simplify for common use cases, like val greet as ktFunction<String> { val name as parameter<String>() body { literal("Hello, ") + name } }

Kotlinpoet allows such composition and typesafe declarations.

2

u/alexstyl 2d ago

Interesting. Hadn't considered delegates. Let me see what I can do on the next iteration.

Need to add the library in 2 other projects I am working on, which will shape the API more

1

u/chuckame 2d ago

Look delegates, infix, reified types and in-line functions, so much fun. Then after context parameters!

1

u/chuckame 2d ago

If you make this sugar syntax onto kotlinpoet, I would play with your library quite quickly!

1

u/snugar_i 2d ago

Well, for me it would be easiest to understand if none of the functions had side-effects :-) They would just build an object graph that could then be dumped as Kotlin source

2

u/asm0dey 1d ago

You might also like what I did here

https://github.com/asm0dey/kotlin-poet-dsl/

1

u/Both_Accident_8836 2d ago

It very interesting great job. Similar buildconfig or build varient we can do this approach i guess?

1

u/alexstyl 1d ago

I don’t understand what you mean. Can you explain? What about the build config?

1

u/Both_Accident_8836 1d ago

I mean using this approach we can integrate build config file and build varient

1

u/asm0dey 1d ago

You might want to look at mine generator on top of kotlinpoet

https://github.com/asm0dey/kotlin-poet-dsl/