Skip to content
This repository was archived by the owner on Apr 25, 2025. It is now read-only.

Missing motivation for rtt.canon #215

Closed
RossTate opened this issue May 12, 2021 · 1 comment
Closed

Missing motivation for rtt.canon #215

RossTate opened this issue May 12, 2021 · 1 comment

Comments

@RossTate
Copy link
Contributor

RossTate commented May 12, 2021

It seems there are a number of languages/applications that would need to actively avoid using rtt.canon:

  • According to No room to specify JS/embedder-specific prototype #211 (comment), the expectation is that languages/applications wanting rich JS interop would import custom rtts rather than use canonical ones so that the rtt can be decorated with the necessary interop functionality.
  • Any application wanting strong data encapsulation will need to avoid not just rtt.canon but even standard structural types, instead using "private" types. (Note that strong encapsulation would normally be ensured by default due to wasm's type system, but rtt.canon breaks it, as pointed out in rtt.fresh will not provide data abstraction #178. So private types are fixing a problem created by rtt.canon.)
  • If an application wants strong data encapsulation for only some struct fields, or if an application wants extensible strongly encapsulated structures (the theory paper behind private types has no subtyping), or if an application does not want dynamic wrapping/unwrapping at boundary points, then private types are insufficient, and an entire nominal hierarchy is required. Note that there is a TC39 Stage 3 proposal for class fields that makes a point of stating that private fields will be strongly encapsulated, and there is another TC39 Stage 3 proposal for private methods, which each suggest there is demand on the web for strong encapsulation.

It seems there are a number of languages/applications where rtt.canon would be unnecessary:

  • For languages with reified generics with variance (such as Java/Kotlin/Scala/C#1.0, where the generics are only language-defined, and C#2.0+, where they are also user-definable), the casting algorithm needs to be recursive on the runtime-type-representation in order to accommodate variance. As a consequence, it is fine for two instances of Array<String> to have different runtime-type-representations of Array<String> so long as they have the same "magic numbers" representing the unary Array type constructor and the nullary String type constructor.
  • For languages with erased generics, you cannot rely on rtts at all because instances of string * string might be constructed by polymorphic functions like zip : 'a list -> 'b list -> ('a * 'b) list that do not have access to the rtts for 'a or 'b. (Note that this means that OCaml/Haskell cannot compile to the Post-MVP using type-erasure because they permit expansive-recursive types which are undecidable with iso/equi-recursive types.)

It seems there are a number of languages/applications that could utilize some form of type canonicalization not sufficiently covered by rtt.canon (and generally better served through in-app implementation):

  • While many languages do not need type canonicalization, it often still has value. For one, it does mean that a fast path for casts where rtt identities are compared are more likely to hit. But much more importantly, type canonicalization is useful because the runtime-representation of types is often quite large, e.g. containing the v-table, interface table, reflection information, and so on. However, rtt.canon doesn't help with any of that—even its casting coverage is insufficient for nominally typed languages. So these languages need to develop their own in-house solution for type canonicalization anyways, and one that canonicalizes according to surface-level types rather than low-level types. (Furthermore, a common way to do this for languages where arrays are the only generics is to have, say, the VTable<\alpha> structure have a nullable VTable<Array<\alpha>> field storing the canonical v-table for arrays of \alpha—but this is an expansive-recursive structure and so not practically compatible with equi-recursive types.)
  • Similarly, languages/applications wanting rich interop with JS are going to want to canonicalize their JS decorations (whether part of the rtts or part of some specified-in-JS prototype) somehow, and rtt.canon does not provide that infrastructure in any way.
  • Modules from languages supporting separate compilation with weak data encapsulation will only know a portion of the structures defined by other modules and so will not have sufficient information to describe the type to construct a canonical rtt of. Instead, they will have to import the relevant types/rtts (and possibly field-references/member-offsets).

Of course, we also know that any closed program can easily replace all rtt.canons with global rtts initialized by rtt.fresh, so it has no value outside of infrastructure. But, due to the above use cases (which seem to cover the most predominant and high-priority use cases), it seems that any infrastructure for WebAssembly—tooling, static linking, dynamic linking, engine implementations, embedding APIs, and so on—will need to handle programs not using rtt.canon. So what is the motivation for why rtt.canon is "definitely needed"? I couldn't find any motivations provided in the Overview or MVP documents, but I could find multiple requests for concrete examples that would motivate rtt.canon (over rtt.fresh) that went unanswered: #86 (comment) #129 (comment) #156 (comment)

@tlively
Copy link
Member

tlively commented Feb 16, 2022

I'll close this in favor of #275, which is largely similar but is based on concrete implementer feedback and the details of the MVP type system we have settled on.

@tlively tlively closed this as completed Feb 16, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants