request for simple interface inheritance?

218 views
Skip to first unread message

jon...@gmail.com

unread,
Jul 20, 2020, 12:08:12 AM7/20/20
to Cap'n Proto

I understand real inheritance is a hornets nest or complex weirdness, and I don't need that.

but could we add a really simple version?

like all the msgs have a common 5 fields in them. make the common fields an interface that all my msgs inherit from?

I don't expect to ever instantiate a concrete version of the "base" class. I just want to pass the derived msgs to a function that expects the base-type.

Kenton Varda

unread,
Jul 20, 2020, 8:00:31 PM7/20/20
to jon...@gmail.com, Cap'n Proto
If you have a struct Foo whose fields are a strict superset of some other struct Bar (with matching field numbers and everything), then you can convert one to the other like:

    Foo::Reader foo = ...;
    Bar::Reader bar = capnp::AnyStruct::Reader(foo).as<Bar>();

(Or vice versa.)

But the big problem with this is that you can't ever add a new field to Bar, because it would force you to renumber all the later fields of Foo, which would be a backwards-incompatible change.

In general I instead recommend that Foo should contain a field of type Bar. This seems to suit most use cases just fine.

BTW, note that RPC interfaces in Cap'n Proto do support inheritance. It's only structs that don't.

-Kenton

--
You received this message because you are subscribed to the Google Groups "Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to capnproto+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/capnproto/e08ca0bc-b386-4d04-b77d-569be721009do%40googlegroups.com.

Jon Ross

unread,
Aug 13, 2020, 10:55:54 PM8/13/20
to Cap'n Proto
> But the big problem with this is that you can't ever add a new field to Bar

Yes you can, if it's just an interface and the fields need to exist individuals in each message. If you have a field in bar call "theThing", and it's id is 7 in one message, and 14 in another that's fine, as long as it exists. simple for the compiler to check that all fields in bar exist in any message that "extends/inherits" it. at runtime it's just a interface you can slap on any message that uses it. If you want to add a new field to bar, you have to add it to all the messages individuals, and their ids will be specific to that message.... at least that's how I'd do it....

Kenton Varda

unread,
Aug 14, 2020, 11:06:09 AM8/14/20
to Jon Ross, Cap'n Proto
Hmm, would `Bar` be allowed to be used as a type in schemas? If so, when you receive an arbitrary `Bar` on the wire, how would you figure out which layout to use?

If `Bar` is not allowed on the wire then I can imagine how to implement this, but I think a lot of people would be surprised by such a restriction.

-Kenton

Jon Ross

unread,
Aug 14, 2020, 2:21:58 PM8/14/20
to Cap'n Proto

I don't quite understand. You would never see a `bar` on the wire, just like you can't instantiate a interface/abstract-class in a OO language. You can only send concrete types on the wire. You just have the ability to downcast to a `bar` at run-time.

In java lingo I'm really just asking for "implment bar" to be added to the class defs of the message types, and to define "bar" as a interface someplace. I don't think it has much (any?) meaning outside of oo languages.

Kenton Varda

unread,
Aug 14, 2020, 3:08:17 PM8/14/20
to Jon Ross, Cap'n Proto
Right, so, what you're asking for isn't necessarily the same thing as what other people asking for "inheritance" (even "interface inheritance") typically want.

A lot of people who ask for inheritance specifically want to be able to do this:

    struct interface Bar { ... }
    struct Foo implements Bar { ... }
    struct Baz implements Bar { ... }

    struct Message {
      value @0 :Bar;
      # May contain a Foo or a Baz.
    }

I think if we added a way to declare "struct interfaces" in capnp schema language, people would be confused to find that they can't actually use the interface type in their schemas.

It sounds like you're fine with that -- all you want is to be able to write code in whatever programming language which is polymorphic over struct types that have the right common subset of fields.

I think the right way to solve your problem really depends on the language:

- In dynamic languages, you just use duck typing.
- In C++, you can use templates (which provide compile-time duck typing).
- In languages with implicit interfaces (e.g. Go and Haskell) you can declare the interface in code and the capnp types will implicitly implement it.
- In Java, perhaps you need an annotation which you can use to tell the code generator to please declare the reader/builder as implementing some interface:

    struct Foo $Java.readerExtends("com.example.Bar") { ... }

This would cause the code generator to add the appropriate "extends" declaration to your reader type; it's up to you to define the Bar interface separately.

-Kenton

Reply all
Reply to author
Forward
0 new messages