On the dev status of gRPC We need it for one thing...
# kotlinx-rpc
a
On the dev status of gRPC We need it for one thing - break things while we still can. We want to iterate quickly on the first feedback so that when we merge it into main releases, we have fewer breaking changes and our stabilisation can be smoother and faster. For example, right nullability and presence in the Protobuf implementation are non-optimal, and we want to change the feature completely. Oneofs are a bit too verbose. The Extensions API needs some care. And other things. So if anything feels not right, out of place, or just wrong, please tell us so we can fix it quickly! I'm always here just to chat about the lib too! To share our views and ideas, or just for a discussion!
g
i would suggest in your examples to add the imports as well if possible I spent some time figuring out why the message dsl was not working based on the example and it was because of missing the
invoke
method. Not sure if its expected though and intellij did not really help
a
Good point. It's a known IntelliJ annoyance. I'll add imports to the examples, and will ask if the core issue can be addressed
thank you color 1
g
feedback: oneof fields generating value classes makes working with them a bit harder since we cannot rely on smart casts and we need an extra variable for them
Copy code
message Demo {
  message Version {
    string version = 1;
  }

  message Release {
    string release_date = 1;
  }

  oneof request {
    Version one = 1;
    Release two = 2;
  }
}
generates
Copy code
interface Demo {
    val request: Request?

    sealed interface Request {
        @JvmInline
        value class One(val value: Version): Request

        @JvmInline
        value class Two(val value: Release): Request
    }

    @GeneratedProtoMessage
    interface Version {
        val version: String
    }

    @GeneratedProtoMessage
    interface Release {
        val releaseDate: String
    }
}
which does not allow the following:
Copy code
class DemoImpl: DemoService {
            override suspend fun RunDemo(message: Demo): DemoResponse {
                when (message.request) {
                    is Demo.Request.One -> {
                        message.request.value.version
                    }
                    is Demo.Request.Two -> {
                        message.request.value.releaseDate
                    }
                    else -> error("")
                }
            }
        }
and we need a new variable for
message.request
👀 1
thank you color 1
a
On it! Right now in the process of looking back at our oneof generation
g
Thanks, In general my impression is very positive
kodee happy 1
a
I'm very glad to hear that! Since we are here, I want to hear your opinion on the explicit presence issue, if you don't mind. Right now, we have nullable fields in some places and a presence object with hazzers in others. We want to provide a consistent model, which accounts for both Kotlin nullability and Protobuf's presence and custom defaults. Our working idea now is to: • Generate all fields as not nullable • For optional fields generate both hazzers like we do now and
orNull
versions So that you would get something like:
Copy code
@GeneratedProtoMessage
interface MyMessage {
    val some: String // optional in .proto
}

val myMessage: MyMessage
val a: String = myMessage.some // returns default value when absent
val b: String? = myMessage.someOrNull // returns null
myMessage.presence.hasSome()
Would you find this approach a good one? This would also imply that in builders we would need to provide
clear
functions:
Copy code
myMessage.copy {
    some = "value" // OK
    some = null // not possible by type mismatch
    clearSome() // replacement for '= null'
}
g
That sounds good from a first look. The presence wouId be only for optional fields, correct? assume this would also work for nested messages?
a
Yes, correct
Also, slight correction: for all fields with explicit presence tracking (which is default in editions for example)
u
If I may chime in with a thought: whatever is done here, keep the case of not accounting for null/absent values efficient (in terms of wire format footprint, processing performance, ease of use). In my mind, interop of different systems and (protobuf) versions when it comes to nullability can be challenging, as they may use different approaches. An application developer may have the power to eclipse the issue, and not expose nullability concerns to the proto layer at all. I don't want to be forced to deal with nullability issues when I've specifically structured my data to avoid them. And I don't want the wire format to get inflated over it, or the risk of millions of short-lived objects bringing down the GC, because an intermediate layer is generic enough to create them for null handling.
a
Yes! And that is exactly what we want to achieve with this design Keep protobuf cases easy and intuitive to use (
message.field.nested1.nested2
, no nulls), but add nullable options (
fieldOrNull
) for people who really like Kotlin's nullability handling (https://github.com/protocolbuffers/protobuf/issues/10178)
❤️ 1
u
Sounds great. I found protobuf and system interop nullability concerns painful to think about in the past, so I decided to handle it myself.
true 1