Nominal for Storing, Structural for Manipulating

todsacerdoti | 49 points

> What this means is that all nominal variants and records are really just nominal wrappers over structural variants or records.

If you have both nominal types and structural types in your language, you can already do this, while keeping the ability to be nominal only or structural only when you want.

In the following OCaml code variant inference in pattern matching is not automatic (although the compiler will warn you about the missing cases, which helps you figuring what to write), but the types do get refined in the corresponding branch.

    type 'a tree =
      Tree of
        [ `Empty
        | `Branch of ('a tree * 'a * 'a tree)
          (* Unfortunately, inline records are not supported here,
             so you have to use tuples, objects, or a mutually recursive record
             type. *)
        ]
      [@@unboxed]

    (* You can give aliases to subsets of constructors *)
    type 'a branch =
      [ `Branch of ('a tree * 'a * 'a tree) ]
    
    let f (x : 'a branch) = ()
    
    let f x =
      match x with
      | Tree `Empty -> ()
      (* You need to be explicit about the cases you want to handle. This pattern
         could also be written `Tree (#branch as rest)`. *)
      | Tree (`Branch _ as rest) -> f rest
The one feature I'd really like in this space would be the ability to refer to a subset of the constructors of a nominal types as if it was a (restricted) polymorphic variant that could only be recombined with another subset of constructors of the same nominal type. It would allow some of the power of polymorphic variants without losing the efficient representation allowed by nominal types knowing the possible variants ahead of time.
deredede | 7 months ago

The examples at the start seem confused & poor. A type that "can return either one result, many results or an error" is trying to fit two different cardinalities into a single API.

APIs should either be typed to be unary (possibly with optionality/ error), or plural (allowing 0..many).

I've dealt with similar woolly design before. Introducing clear distinction between cardinalities gave a major improvement in logical clarity.

twhitmore | 7 months ago

The nominal vs structural distinction (in both programming and math) goes beyond types. Should names in programs matter?

Consider two functions, say copySurname and copyStreetName. They do same exact thing, but in different context. No sane person would call copySurname for the street, even though the function is the same.

So there is this tension, should name of the function (or of the type) only reflect it's internal structure (structuralism), or should it also reflect the intended domain application (nominalism)?

There is a formalism school of mathematics that is pretty much the hardcore structuralist, i.e. names of the objects don't matter, only their relationships. But most mathematicians (and all programmers) reject that view.

js8 | 7 months ago

I think is forgotten here that one of the benefits of nominal typing is that the compiler can know that data layout at run time so performance benefits.

There has been so much ink spilled on the question of what kind of type systems help programmers be productive but there is not such controversy on the performance side.

PaulHoule | 7 months ago

I guess Modula-3 was doing it as well.

Records were structurally typed. But you can "braid"(?) a record and that will make it nominal type.

vrotaru | 7 months ago

An odd to see Java thrown in there without methods...

readthenotes1 | 7 months ago

ASN.1 is structurally typed too.

cryptonector | 7 months ago
[deleted]
| 7 months ago

convertTree doesn't work because Tree uses Tree type recursively.

molikto | 7 months ago