i think they can be useful internally within a module as a way of simulating C++ template constants. i don’t think there is a good reason to expose them (permanently) as public API although i’ve definitely shipped them before due to time constraints. using types as values is just not really using types for what they are meant for.
This pattern is often called “phantom types” and is also used in other programming languages too, for example Haskell, Rust, TypeScript, Elm, and Scala.
I can't see how "adding a type parameter to a type definition without changing its structure" relates here. Am I missing something? Can you elaborate on this?
I'm not sure whether this relates to the topic. But at Swift runtime, we can call type(of:) to get the type values, so the phantom type does have effects at runtime.
Can you explain more about this? I'm really confused right now.
It's more that the type won't take any physical storage (metadata notwithstanding) because it's only being used at compile time, the type not existing at all at runtime is either an oversimplification or a Scala-ism.
Of course, if the compiler can prove that you aren't doing anything with the type at runtime it's free to elide the metadata as well.
This may well be a use of Phantom Types, but it's not the usual use since it's being used it to pass around static variables. Normally Phantom Types are used to distinguish between types that would otherwise be identical. Like the Tagged library does for it's newtypes.
You’re right. After looking more closely at the examples in your posts, I see that I was mistaken. These aren't phantom types. The type objects are needed to provide functions (as static methods or properties), presumably so that (unlike bare functions) they can be compared for equality during view reconciliation.