Nice write-up, and good feedback. I'd push back a little bit on the idea of "design for the call site", i.e. that ergonomics of application code take precedence over that of the schema language. My main concern with that notion is that schemas represent an interface, and interfaces need to be read and understood by many more people than implementations. For that reason I'd argue that it's important that schemas can be expressed as clearly as possible, with the ability to organize features semantically for presentation to a reader.
One thing that is part of that is making sure related declarations can be grouped together, so that they can easily be read and digested together, without scrolling back and forth. I'd say this is the main motivation for nested declarations -- they can live next to the field or method that references them, rather than far away at the global scope. It's also the motivation for inlined method parameters and results, rather than requiring separate structs. Moreover, personally, I often write schemas as a way to organize my thoughts and lay out a design, before I start implementing. Here, again, as I organize my thoughts and rapidly change my mind about details, I think it's important that I don't have to scroll up and down constantly. That said I do sympathize with the argument that having to constantly cross-reference other files while writing (or reading) code is painful. I still have trouble writing a method definition without looking at the generated header. I rely a lot of my IDE's autocomplete and jump-to-definition to make things more bearable. 10 years ago it would have been a lot more difficult. ---------- Regarding inheritance, I do think multiple inheritance is a must-have. Looking at Sandstorm, there are 23 interfaces that inherit other interfaces, and fully 16 of them are multiply-inherited. Some of them could perhaps have been replaced by composition. For example, VerifiedEmailSendPort could have inherited nothing and instead had two methods, getVerifiedEmail() and getSendPort(). Arguably that's even a better decide regardless, so that the capabilities can be passed on separately. (Though, of course, it's always possible to accomplish the same with membranes, albeit with higher cost.) However, most of the multiple inheritance is to add persistence. The `Persistent` interface is essentially a mix-in, used to add a save() method that returns a token which can be used to get the same capability again. https://github.com/capnproto/capnproto/blob/master/c++/src/capnp/persistent.capnp You might argue that Persistent should also be composition-based: it could have a method that you call to get the actual object. However, this would mess up a lot of interface designs in Sandstorm. In lots of places, it turns out, interfaces don't really know if the capabilities they are passing are persistent or not. You'll see doc comments saying "this is persistent if X and Y are true". Sometimes it's fully expected that applications need to probe for save() support at runtime. I'd say the underlying reason it ends up this way is because persistence is fundamentally an orthogonal concern from business logic. Persistence is a logistical issue. It is very natural, then, for it to be a mix-in. You could maybe argue that persistence should be baked more directly into the system. Maybe save() should be a method on Capability, even though not all objects will implement it. But, that's actually how I originally thought of persistence -- when I defined "level 2" of RPC to be persistence, I had imagined it being baked into the protocol. I ended up very happy that it didn't have to be; that persistence could be entirely defined in a separate, higher layer. Ultimately I don't think there is a single obvious design for persistence -- I think the concerns are, for example, very different between Sandstorm apps vs, say, people talking over the public internet. Even within Sandstorm, there are different realms of persistence which called for slightly different interfaces (SystemPersistent, AppPersistent, etc.). I also expect that there are features other that persistence which might be similarly orthogonal to business logic, which people will want the ability to mix-in in a similar way. So I remain pretty happy with the decision to support multiple inheritance. -Kenton On Mon, Jun 24, 2019 at 10:22 PM Ian Denhardt <i...@zenhack.net> wrote: > Hey all, > > A few weeks ago in the thread about the Elm implementation, I mentioned > that I had a longer critique of the schema language that I'd been > meaning to write up. I finally got around to it; the blog post is here: > > > https://zenhack.net/2019/06/25/a-critique-of-the-capnproto-schema-language.html > > Cheers, > > -Ian > > -- > 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+unsubscr...@googlegroups.com. > Visit this group at https://groups.google.com/group/capnproto. > To view this discussion on the web visit > https://groups.google.com/d/msgid/capnproto/156143984544.21360.5023497358389493172%40localhost.localdomain > . > -- 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+unsubscr...@googlegroups.com. Visit this group at https://groups.google.com/group/capnproto. To view this discussion on the web visit https://groups.google.com/d/msgid/capnproto/CAJouXQnkv7TVz86_ofKcsZ%2BtVxPGVGt7tF8fcAVivSMnDqoP5Q%40mail.gmail.com.