#Why does Gleam does not have subtypes or unions?
1 messages · Page 1 of 1 (latest)
subtyping is largely a feature of object-oriented languages, of which gleam is not one. We could have structural subtyping but louis decided against this to encourage clear nominal types.
wrt unions, they require runtime type checks and/or fairly sophisticated flow analysis to statically type them. the erlang and elixir communities have already soft bought into the idea of tagged unions through heavy use of tuples with atoms as their first element, eg {:ok, 1} this maps neatly to gleam's custom types: using untagged unions would just mean everyone re-invents the wheel to follow the patterns that are already idiomatic (eg see typescript and everyone doing type Thing = { tag: 'Foo', ... } | { tag: 'Bar', ... })
one of gleam's strengths, imo, is that its simple both in semantics and in implementation - some things become harder or more awkward to express as a consequence but i think the trade-off is worth it
Additionally:
I think subtypes make languages a lot harder to use. Concepts like covariance and contravariance are required knowledge yet they are complicated and easy to misunderstand. Error messages become much less clear. HM type checking is no longer really possible. Overall it would not contribute to the experience we’re looking to build, we would likely instead have a much less satisfying experience like Java or TypeScript.
Untagged unions in reality mean that your runtime tags everything for you. In Java that is the case, so you may as well take advantage of that cost you have already paid. On Erlang it is done only so much as you know what primitive type something is, so we only know a tuple is a tuple and a reference is a reference. The problem here is that multiple types have the same runtime representation! A timer reference has the same runtime representation as a monitor reference, we cannot tell them apart. This leaves us with these options:
- Dramatically reduce the precision of the type system by making is structural, and remove the ability to build nice typed APIs over these ones. Gleam programming becomes much more confusing and error prone.
- Start tagging everything. Gleam code becomes slower and FFI becomes very difficult. Using Gleam from other languages practically becomes impossible.
Start tagging everything.
is that not what the compiler does today anyway?
Compile time yes, runtime no
An int is just an int at runtime
Is it currency, a database id, or a timeout? Dunno
well sure but if you introduce new gleam types to disambiguate between those
type Currency { Currency(Int) }
type DatabaseId { Id(Int) }
type Timeout { Timeout(Int) }
at runtime aren't these {:currency, int} {:id, int} and so on?
They are
I’m talking about external types for ints there
For non external types the problem remains for tuples and atoms
Is it a tuple with an atom in the first position or a record? No way to tell
Option 3: only permit unions with structurally distinct types. I think this would be a very frustrating and confusing experience
yeah that'd suck
Someone has been writing Elixir 😁
Or reading
That seems more likely
neither 😮 im just slowly absorbing the syntax through osmosis here
doesn't TS have the same problem though? numbers are just numbers etc. but it allows for number | string and stuff and if you stay completely within TS then you can trust it's correct.
It doesn’t because all types in TS have unique runtime representations
And also TS does not provide the UX I am aiming for with Gleam. It’s very complicated and far from as helpful a compiler
Sounds like with Gleam we have a good chance to capture some JS ground.
i dont hear many people complaining about ts really
maybe that's because they used to write js 😄