I think it also makes more sense in immutable functional languages like clojure. Oddly enough I like it in Go too, despite being very different from clojure.
It seems ok in upcoming languages with polymorphic sum types (eg Roc “tags”) though?
Reading TFA now, Pythons NewType seems to be equal to Haskells newtype. Yes, it's a hack for the type checker to work around existing language semantics and feels unergonomic at times when Parse, Don't Validate needs to fall back to plain validation, but I wouldn't call it neither weird nor arbitrary.
The kind of "branding" I'm talking about is a hack only needed for structural typing systems. Consider something inspired by the C locale API, for example:
class RealLocale:
name: str
const C_LOCALE = RealLocale("C");
# Each of these can be passed to *some*, but not all, locale functions,
# which will check for them by identity before doing the logic for `RealLocale`.
singleton NO_LOCALE # used for both "error" and "query"
singleton THREAD_LOCALE
singleton GLOBAL_LOCALE
singleton ENV_LOCALE
In a structural typing system, it is impossible to write a function that takes a union including more than one of `{NO_LOCALE, THREAD_LOCALE, GLOBAL_LOCALE, ENV_LOCALE}`, since they have no contents and thus cannot be distinguished. You have to hack around it, by some kind of casting and/or adding a member that's not actually present/useful at runtime.And this kind of need is quite common. So I maintain that structural typing is not a serious type system proposal.
(Then again, the man proponent for structural typing is TypeScript, which even in $CURRENTYEAR still lacks even a way to specify ubiquitous needs like "integer" or "valid array index").