[HN Gopher] Nominal Types in WebAssembly
       ___________________________________________________________________
        
       Nominal Types in WebAssembly
        
       Author : ingve
       Score  : 34 points
       Date   : 2026-03-10 08:27 UTC (4 days ago)
        
 (HTM) web link (wingolog.org)
 (TXT) w3m dump (wingolog.org)
        
       | beepbooptheory wrote:
       | > field access is a bit odd; unlike structs which have
       | struct.get, nominal types receive all their values via a catch
       | handler.
       | 
       | I know this is meant to be silly, and I am no expert, but I kinda
       | _do_ like this syntax. Its like shaking the struct and seeing
       | what falls out.
        
       | tlively wrote:
       | Andy jests, but I would actually like to add nominal types to
       | Wasm (along with type imports to make them usable). No proposal
       | yet, but maybe later this year.
       | 
       | This blog post mentions that you can kind of emulate nominal
       | types by putting all your types in one rec group, but then it
       | brushes that off as inferior to using exceptions. (Which is
       | hilarious! Good work, Andy.) What it doesn't make clear is that
       | people actually use this rec group trick in practice. There are
       | two ways to do it: you can put literally all your types in one
       | rec group, or you can emit minimal rec groups with additional
       | "brand types" that serve no purpose but to ensure the groups have
       | different structures. The former solution is better for code size
       | when the entire application is one module, but the latter
       | solution is better if there are multiple modules involved. You
       | don't want to repeat every type definition in every module, and
       | using smaller rec groups lets you define only the types that are
       | (transitively) used in each module.
       | 
       | The Binaryen optimizer has to ensure that it does not
       | accidentally give distinct types the same structural identity
       | because that would generally be observable by casts. Most of its
       | type optimizations therefore put all the types in one rec group.
       | However, it does have a type merging optimization that takes the
       | casts into account[0]. That optimization is fun because it reuses
       | the DFA minimization code from the original equirecursive type
       | system we were experimenting with for Wasm GC. We also have a rec
       | group minimization optimization[1] that creates minimal rec
       | groups (by finding strongly connected components of the type
       | definition graph), then ensures the types remain distinct first
       | by using different permutations of the types within a rec group
       | and then only as necessary by adding brand types.
       | 
       | [0]:
       | https://github.com/WebAssembly/binaryen/blob/main/src/passes...
       | 
       | [1]:
       | https://github.com/WebAssembly/binaryen/blob/main/src/passes...
        
         | flohofwoe wrote:
         | I'm using WASM via Emscripten almost since the beginning but
         | have never encountered 'rec' or 'struct' (or generally types
         | beyond integers and floats). Why would WASM even need to know
         | how structs are composed internally, instead of 'dissolving'
         | them at compile time into offsets? Was this stuff coming in via
         | the GC feature?
        
           | tlively wrote:
           | Yes, this is all part of Wasm GC. WebAssembly needs to know
           | the structures of heap objects so that a GC can trace them
           | and also to preserve type safety when accessing them.
           | Treating the heap objects as uninterpreted bags of bytes
           | wouldn't have worked because so many of their fields are
           | references, which must remain opaque in Wasm.
        
           | lioeters wrote:
           | I've been learning to use WebAssembly directly, by hand-
           | writing the Lisp-like assembly text, and also compiling C to
           | Wasm without Emscripten or LLVM. It's given me a deeper
           | appreciation for the original specification, version 1. Its
           | technical design is a solid foundation, a lot of good thought
           | went into it: Briging the Web Up to Speed With WebAssembly - 
           | https://github.com/WebAssembly/spec/raw/refs/tags/wg-1.0/pap.
           | ..
           | 
           | In a way it's a complete instruction set and bytecode format,
           | that could have been frozen in time and still be a useful
           | addition to x86, ARM, RISC-V. It's great that the Wasm v1
           | specs is small enough for various implementations to arise.
           | There are Wasm interpreters written in C, Zig, Go, Rust, as
           | well as Wasm to C compiler, disassembler, little languages
           | that compile to Wasm.. I see great value in that simplicity
           | and smallness, it contributes to easier cross-platform and
           | cross-language compatibility.
           | 
           | That's why newer features in the specs after v1, like garbage
           | collection, components, interfaces, feel like they're higher-
           | level abstractions that are not so relevant for my use case.
           | Some feel like they could be developed outside of Wasm specs,
           | like you said, dissolved at compile time to existing
           | primitives. I'm guessing much of the benefit is for
           | integrating with Rust ecosystem, and perhaps other languages
           | gradually.
        
       | kjksf wrote:
       | Good lord. WebAssembly was sold as "portable assembly for the
       | web". It's in the fricking name. Web. Assembly. Assembly for the
       | web.
       | 
       | It was supposed to solve the problem of: some computers run x86,
       | some arm, we need something that is equivalent, but portable
       | across different cpus
       | 
       | What business is it for WebAssembly to know about complex types?
       | What x86 instructions is there for `(type $t (struct i32))` ? Or
       | doing garbage collection.
       | 
       | We would be better off standardizing on a subset of x86 and
       | writing translators to arm etc. Or standardize on arm and
       | translate to x86.
       | 
       | We know it can work. Apple did it with rosetta. Microsoft did it
       | with Prism. I don't think WebAssembly implementation generate
       | faster code than rosetta or prism.
       | 
       | QEMU did it simply (albeit slowly).
       | 
       | WebAssembly is becoming another JVM. It's not simple. It's not
       | fast. It's not easy to use.
       | 
       | But now we're stuck with it and the only path is to add and add
       | and add.
        
         | flohofwoe wrote:
         | I guess you didn't read to the end?
         | 
         | > Friends, as I am sure is abundantly clear, this is a troll
         | post :)
        
         | zffr wrote:
         | > It's not fast.
         | 
         | Not disagreeing with you, but here's an article from Akamai
         | about how using WASM can minimize cold startup time for
         | serverless functions.
         | 
         | https://www.akamai.com/blog/developers/build-serverless-func...
        
         | flohofwoe wrote:
         | > It's not fast.
         | 
         | My emulators here have roughly the same performance as the same
         | code compiled as native executable (e.g. within around 5%) -
         | this is mostly integer bit twiddling code. Unless you hand-
         | optimize your code beyond what portable C provides (like
         | manually tuned SIMD intrinsics), WASM code pretty much runs at
         | native speed these days:
         | 
         | https://floooh.github.io/tiny8bit/
        
         | mavdol04 wrote:
         | I mean standardizing on an x86 subset would replace wasm's
         | native portability with a kind of 'emulated' compatibility, and
         | this is one of wasm's strengths. If we do that, non-x86
         | hardware(mobile etc.) will pay the translation tax. So, keeping
         | Wasm agnostic makes more sense anyway.
        
         | 14113 wrote:
         | This post is actually a joke, but it does bring about an
         | important point: For an interpreter, having _more_ information
         | results in faster execution. WASM is much closer to Java
         | bytecode than you might think, and SpiderMonkey /V8 are
         | basically the JVM. WASM also undergoes multiple different
         | stages and kinds of JIT compilation in most browsers, and
         | detailed type and usage information helps that produce faster
         | execution.
         | 
         | Also, don't forget that WASM is designed to replace JavaScript,
         | thus it must interoperate with it to smooth the transition.
         | Rosetta and Prism also work to smooth the transition from x86
         | -> ARM, and much of the difficult work that they do actually
         | involves translating between the calling conventions of the
         | different architectures, and making them work across binaries
         | compiled both for and not for ARM, not with the bytecode
         | translation. WebAssembly is designed to not have that
         | limitation: it's much more closely aligned to JS. That's why it
         | wouldn't make sense to use a subset of x86 or similar, as it
         | would simply produce more work trying to get it to interface
         | with JavaScript.
        
         | treyd wrote:
         | > We would be better off standardizing on a subset of x86 and
         | writing translators to arm etc. Or standardize on arm and
         | translate to x86.
         | 
         | This is basically what Native Client (NaCl) was, and it was
         | really hard to work with! We don't use it anymore and developed
         | WASM instead.
        
         | Rusky wrote:
         | The types are there for garbage collection, which is there for
         | integration with the Web APIs which are all defined in terms of
         | garbage collected objects.
        
       | rusakov-field wrote:
       | Not familiar with WebAssembly, but from the namesake was
       | expecting the syntax to kind of resemble assembly.
       | 
       | He is showing S-expressions ? That is its' syntax ? I am
       | intrigued now.
        
         | flohofwoe wrote:
         | Real-world WAT (WASM text format) looks more like this (e.g. it
         | looks like a 'structured assembly' type of thing):
         | i32.const 27512         i32.load         local.tee $var1
         | if           i32.const 27404           i32.load
         | local.get $var1           call_indirect (param i32)         end
         | 
         | S-expressions are only used outside such instruction blocks for
         | the 'program-structure' (e.g. see:
         | https://developer.mozilla.org/en-
         | US/docs/WebAssembly/Referen...). IIRC early pre-release-
         | versions of WASM were entirely built from S-expressions and as
         | a 'pure stack machine' (I may remember wrong though).
         | 
         | To see what a complete WASM blob looks like in WAT format you
         | can go here: https://floooh.github.io/sokol-html5/clear-
         | sapp.html, open the browser devtools, go to the 'Sources' tab
         | and click the `clear-sapp.wasm` file).
        
           | tlively wrote:
           | This is partially true, but the standard text format also
           | allows the instructions to be nested as S-expressions, for
           | example:                 (i32.add         (i32.const 0)
           | (i32.const 1))
           | 
           | Many projects, including the official spec test suite and the
           | Binaryen test suite, primarily use this format.
           | 
           | > IIRC early pre-release-versions of WASM were entirely built
           | from S-expressions and as a 'pure stack machine' (I may
           | remember wrong though).
           | 
           | Yes, the S-expressions predate WebAssembly even being a stack
           | machine. Originally the design was that it encoded an AST, so
           | the folded S-expression format was the only option.
           | 
           | There was a lot of discussion back in the day (before my
           | time) about creating a better text format, but no one could
           | agree on what it should be, so they just defaulted to the
           | S-expression idea and focused on getting WebAssembly out the
           | door.
        
       | elophanto_agent wrote:
       | webassembly adding nominal types is like watching a toddler
       | slowly reinvent java. next up: checked exceptions and enterprise
       | beans
        
       ___________________________________________________________________
       (page generated 2026-03-14 23:01 UTC)