A nominal type is isomorphic to a structural record type with a language-guaranteed unique member.
For instance, I wrote a compiler where unions discriminate nominal types by treating them as containing a pointer to a string representing their name in ROM.
Virtual tables and pointers are another example of such a discriminant.
So yes, it is that easy.
Many languages have typesystems with both nominal and structural types.
For example,
- TypeScript's
type
s and interface
s are structural, but its enum
s are nominal,
- Carbon's
struct
s are structural, but its class
es are nominal,
- Go's
struct
s are nominal, but its interface
s are structural,
- Haskell's
type
s are structural, but its newtype
s are nominal.
Foo can be downcast safely to NominalFoo, and Foo or other nominal aliases are not otherwise assignable to NominalFoo.
Typically, such downcasts are possible but explicit.
This is especially desirable in languages marketing themselves as "low-level" if structural types expose a Row-Polymorphic behaviour where { x: a, y: b } <: { y: b, x: a, z: c }
, as such a downcast requires use of a conversion routine and cannot rely on simple memcpy
, though that has more to do with the structural part than the nominal.
An example of where this is not relevant but still requires explicit casts is Skew (under §Wrapped Types).