-
Notifications
You must be signed in to change notification settings - Fork 57
Motivation for banning intertype recursion is not 100% clear #137
Comments
Personally, I am not very familiar with LSP. Generally, just because some IPC schema languages support recursive types (it is obviously possible to support them) does not mean that we should support recursive types. |
I can appreciate the technical difficulties involved and the added complexity of generating efficient conversion code. But I'd like to argue for and urge a reconsideration. Recursive types are really common. Supporting them is important for IT to be useful outside of low level domains like WASI, and to fulfill the promise of seamless language interoperability. If they can't be represented, a lot of code will skip defining any IT types and just pass around byte slices with a custom encoding (Json, Proto/flatbuf, ...) instead, because it doesn't make sense to have 2/3 of your API defined as native interface types and the rest as serialized byte slices. Especially because a lot of the value would come from auto-generating language bindings based on just the IT definitions. So if you need a generator like Protobuf or a json-schema anyway, you'll just keep everything in there, except possibly function and resource definitions. Replacing recursive types with resource handles would be possible, but would kill performance (due to the reasons outlined in the explainer), and would introduce a lot of friction, since you now need to write custom glue code to map back to a sane representation. Consider that even something basic like a JSON-compatible Value type requires recursion.
Representing that with resources doesn't seem feasible (to the point that you'd be much better off just serializing to JSON). As a motivating example: today I tried to sketch out the typings for a plugin system with wit-bindgen. After an hour I had about 5 instances of recursive types, which is incidentally why I noticed that IT doesn't currently plan for recursion. Some of those could be worked around by changing the representation (eg flattening, arenas), but that would be intrusive and require custom glue code for each language again. I do believe that IT has the potential to enabling a truly interoperable ecosystem where you can seamlessly share code between languages. But for that to be realized common basic data structures need to be representable. |
There may be some additional misconceptions here. We do not just 'ban' recursive types. In the base Interface Types language, it is not possible to define any new types. This is justified by a combination of factors: not permitting any user defined types maximizes the potential for interoperability (at some cost of course) and the domain is one of limited trust crossing language boundaries so having a small type language makes that easier to manage. |
Just expanding on that last sentence of @fgmccabe's a bit: one way to express recursive values would be to use resources and handles. They aren't in the IT explainer yet, but prototype support is in |
I did cover that above. Resources/handles are awesome for certain use cases where the data actually represents resources with associated actions or where you only want partial access. But they are basically a hack for cases where you just want to transfer data.
|
Or said another way: resources/handles are great for sharing behaviour, but a very limiting solution for exchanging data. |
Just to be clear, I would not expect interface types to have any meaning outside of adapters. The "only" additional requirement would be allowing the definition of recursive structures. But I don't see how this causes a burden on interoperability. |
WebAssembly is not my domain, but I work with Web IDL a fair amount and I’m curious what this means. I know Web IDL disallows a typedef’s type descriptor from directly being a typedef-ref (which prevents totally tautological definitions) and it forbids dictionaries from recursing in any manner (which I think stems from the risk of default dictionaries expanding infinitely, though I’m uncertain about that). However AFAICT recursive types are generally possible with both sequence and record types at least, e.g. typedef sequence<(DOMString or DOMStringTreeThing)> DOMStringTreeThing; Perhaps I have misunderstood the meaning of recursive type as used by @fgmccabe here? (Since my question is off-topic — and possibly just stems from me totally misunderstanding the subject — I’ll hide it later, but this piqued my curiosity at random and maybe it’s a quickie to answer.) |
@theduke oops, sorry, I missed that. Yes, no argument that they're not as performant or ergonomic. I think the current design could be relaxed to allow recursive type definitions, and there would be some clear expressivity benefits, but they also make the implementation (particularly around adapter fusion) significantly more complicated, so we were inclined to hold off on this in the initial "MVP" version. This has the added benefit of, in the meantime, letting us benefit from the significant, ongoing work in the GC subgroup around defining structural recursive types and their subtyping1,2. @bathos The dictionary restriction seems fairly general (specifically the list starting with |
@lukewagner I don't know if it's leveraged anywhere currently. My own Web IDL tools permit it because I aimed for spec compliance and, as far as I can tell, the spec also does. Dictionary is the only "includes" definition that pierces generic params like that, but I've never been sure why. It's true that the spec could just be omitting intended constraints though. Notably, the specified constraint about typedefs doesn't prevent tautological nullable, annotated, or union types. Since it would be a paradox if those weren't forbidden, there is at least one example of constraints being left unstated, which does hint at the possibility of others. Edit: I just realized the spec is arguably sound regarding tautological nullable, annotated, or union types given rules associated with the definitions of those types (nullable inner cannot be nullable, union cannot contain itself, and annotations are applicable to only specific types). FWIW, supporting such cases in my tools did not seem to change much at all, but maybe that's just because I only had to deal with the abstract definitions + the ES conversion algorithms, not serialized representations etc. |
Just for clarity; are recursive datatypes themselves the problem, or the fact that they might form circular references at runtime? The former does not automatically introduce the latter. |
@badeend neither. Although circular structures are definitely not wanted. It has more to do with the intended scope of interface types. Recall that values are coerced across the function boundary. That implies copying the recursive structure during a function call. |
Mostly drive by observation, but the explainer currently says:
I am not sure it’s fair to say that the restriction (logically) follows from IPC requirement: there are IPC/RPC systems which are fine with recursive type. To give one example from a domain I am familiar with, DocumetSymbol from language server protocol is sent over the wire and is recursive. Apparently, protobuf also allows recursive types: https://stackoverflow.com/questions/5945136/self-referencing-fields-in-protobuf-messages
The text was updated successfully, but these errors were encountered: