Hi everyone! If you are using Ktor in fullstack a...
# ktor
c
Hi everyone! If you are using Ktor in fullstack apps (multiplatform server and clients, or micro-services that communicate together), you may have noticed that it can sometimes be painful to synchronize DTOs / make refactors across system boundaries. Locations help a bit, but they use dark magic and are only typesafe for the request, not the response. As an alternative, I'm developing a small library that lets you declare Ktor endpoints in shared code, and then refer to it in server-side and client-side code, with no repetition. If you change the type of an endpoint, you can't accidentally use the wrong type anymore. I'm happy with how it looks as a proof-of-concept, and I'm searching for feedback before stabilizing it. If that seems like it could be useful to you, please get in touch šŸ™‚ Repository • Documentation (far from being complete!) • Full example
kodee happy 6
šŸ‘ 1
p
doesn't kotlinx.rpc address the same problem, just in a bit different way? it also allows defining the API of the service in the common code, so that it's in sync between the server and the client. I think it makes sense to describe the difference in README
c
I haven't heard much about kRPC yet, so I don't know exactly what it can or can't do... Spine is very lightweight, it's just a way to reuse Ktor stuff from shared code. It's not a new framework, it doesn't require large changes to your codebase, and it doesn't use code generation, annotations or compiler plugins.
šŸ‘ 2
p
ok, just signalizing there can be some overlap!
šŸ‘ 1
s
Arrow OMG. If you create a library to simplify something, don’t use Arrow.
c
Ahah, that's an interesting take. Why would that be? Also, Arrow is 100% optional in Spine.
s
90% of potential target audience will like WTF is this
Copy code
suspend inline fun <reified Out : Any> SpineResponse<Out>.body(raiseIn: Raise<HttpResponse>): Out =
	bodyOrNull() ?: raiseIn.raise(httpResponse)
c
Everything from your example but the
Raise
type comes from Ktor and is a direct consequence of the way Ktor endpoints are defined, though. The complexity here has nothing to do with Arrow.
This is from Ktor itself:
Copy code
public inline fun <reified R : Any> Route.put(
    path: String,
    crossinline body: suspend PipelineContext<Unit, ApplicationCall>.(R) -> Unit
): Route = put(path) {
    body(call.receive())
}
Based on the fact that my target audience is Ktor users, and they clearly are not bothered by this, then no, I don't think 90% of my target audience will be confused šŸ™‚ If anything, Spine's version is simpler than the official one šŸ˜…
But again, using Arrow is 100% optional. Spine works exactly the same without it.
s
I meant exactly the Raise. Good that Arrow is optional, thing like that belong to private projects. Also, there is a social component to this. f.e. if I see a library that indicates the author loves functional programming too much, I pass because from experience such libraries rarely get successful.
c
I'm confused. Kotlin is a functional language. Ktor is a functional library. Arrow is one of the most popular libraries in the ecosystem.
s
If is fine if Arrow is tucked deep in the lib and not exposed to the end user, but having users understand Arrow limits your audience.
I read somewhere that one of the designing principles of Kotlin is readability (in contrast f.e. C# is designed as ā€œleast surpriseā€ and Haskell ā€œpure mathā€)
So Kotlin is most readable language first, and functional second.
c
Do you think this is puting functional before readability?
s
You lost me on the second line šŸ˜‰ ā€œraiseā€
Otherwise, looks good
c
Yes, because "raise" is famously harder to read than
PipelineContext<Unit, ApplicationCall>
. Do you even know what that
Unit
type parameter does? 'cause I have no idea, and it has never made me say Ktor is unreadable.
Otherwise, looks good
Welcome to Arrow! You understood what it does without needing to look at the documentation. Please do look at it one of these days, you may find that you enjoy it.
s
In my code, I don’t use PipelineContext either. Just routing get(ā€œxxxā€) etc.
Anyway, gotta go. Thanks for the conversation!
c
We now have our own Slack channel for this project (and many others!) → #opensavvy