Nominal for Storing, Structural for Manipulating
The article compares nominal and structural type systems in programming, advocating for Polaris, which combines both types to enhance flexibility and usability while maintaining data abstraction and type safety.
Read original articleThe article discusses the differences between nominal and structural type systems in programming languages, highlighting their respective advantages and disadvantages. Nominal types, found in languages like Java and Haskell, rely on type names for identity, which aids in data abstraction but can be restrictive and cumbersome for users. In contrast, structural types, common in TypeScript, allow for more flexibility as types are considered equal if their definitions match, but this can lead to issues with data abstraction and error messages. The author proposes a hybrid approach used in the Polaris language, which combines the benefits of both systems. In Polaris, nominal types are implemented as lightweight wrappers around structural types, allowing for both abstraction and flexibility. This design enables users to manipulate types more easily while maintaining the advantages of nominal typing, such as clearer error messages and type safety. The article also mentions features from other languages, like Gleam's variant inference, and asserts that Polaris offers a more powerful and consistent implementation of type refinement during pattern matching. Overall, the author advocates for a balanced approach to type systems that leverages the strengths of both nominal and structural types.
- Nominal types provide data abstraction but can be inflexible and cumbersome.
- Structural types allow for flexible manipulation but can expose entire definitions, complicating data abstraction.
- Polaris combines nominal and structural types, offering both abstraction and flexibility.
- The type system in Polaris allows for refined type matching, enhancing usability.
- The article critiques existing implementations and promotes Polaris as a superior solution.
Related
Evolving languages faster with type tailoring
A proposed solution called "type tailoring" enhances type systems using metaprogramming, allowing better type inference for constructs like regex. The approach aims to improve programming language usability and efficiency.
Why Polars rewrote its Arrow string data type
Polars has refactored its string data structure for improved performance, implementing a new storage method inspired by Hyper/Umbra, allowing inline storage of small strings and enhancing filter operation efficiency.
Design Patterns Are Temporary, Language Features Are Forever
The article examines programming paradigms, emphasizing that modern features in languages like Java 21 can render certain design patterns obsolete, enhancing code readability and maintainability while simplifying problem-solving.
Sets, types and type checking
The blog post explains type theory and type-checking, highlighting their importance in programming languages for error detection, optimization, and understanding code, while introducing fundamental types and intersection types.
Non-elementary group-by aggregations in Polars vs pandas
The article compares Polars and pandas, highlighting Polars' advanced non-elementary group-by aggregations and efficient API for complex operations, while pandas requires more complicated methods, leading to inefficiencies.
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.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.
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.
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.
Records were structurally typed. But you can "braid"(?) a record and that will make it nominal type.
Related
Evolving languages faster with type tailoring
A proposed solution called "type tailoring" enhances type systems using metaprogramming, allowing better type inference for constructs like regex. The approach aims to improve programming language usability and efficiency.
Why Polars rewrote its Arrow string data type
Polars has refactored its string data structure for improved performance, implementing a new storage method inspired by Hyper/Umbra, allowing inline storage of small strings and enhancing filter operation efficiency.
Design Patterns Are Temporary, Language Features Are Forever
The article examines programming paradigms, emphasizing that modern features in languages like Java 21 can render certain design patterns obsolete, enhancing code readability and maintainability while simplifying problem-solving.
Sets, types and type checking
The blog post explains type theory and type-checking, highlighting their importance in programming languages for error detection, optimization, and understanding code, while introducing fundamental types and intersection types.
Non-elementary group-by aggregations in Polars vs pandas
The article compares Polars and pandas, highlighting Polars' advanced non-elementary group-by aggregations and efficient API for complex operations, while pandas requires more complicated methods, leading to inefficiencies.