<p>In, for example, Python, it is possible to subtype primitive types:</p>
<pre class="lang-python prettyprint-override"><code>&gt;&gt;&gt; class Doublable(int):
...     def double(self):
...         return 2 * self
... 
&gt;&gt;&gt; d = Doublable(4)
&gt;&gt;&gt; d == 4
True
&gt;&gt;&gt; d is 4
False
&gt;&gt;&gt; d.double()
8
</code></pre>
<p>The same is true of other primitive types, like <code>bool</code> or <code>str</code>, and I have actually seen string subtypes in the wild.  However, that design felt off to me—if I had written the code, my inclination, despite the higher up-front effort, would have been to declare a new type with a string field and then write methods to imitate the needed string-like behavior.</p>
<p>In a language where forbidding subtypes is possible and where the language design is meant to encourage maintainable code (so not an esolang or golflang), are there concrete practical situations where a user of the language not being able to subtype one of the usual primitive types (the unit type, the boolean type, numeric types, character types, perhaps built-in collection types or collection-like types such as an option type…) is a serious problem?</p>
<p>In languages with duck typing, of course the programmer can just imitate the primitive type, as described above.  But suppose that's not an option—if, say, I have a function that must take an actual boolean, is there any reason that the &quot;right&quot; design would be to pass a value that subtypes boolean rather than doing some conversion to a proper boolean before the call?  (In my particular case, all primitive values are immutable, so if such concerns arise only for mutables like Python's <code>list</code>, that would be good to know.)</p>
<p>I understand that it can be hard to prove a negative; if the answer is &quot;no&quot;, a well-reasoned heuristic argument would suffice as an answer.</p>

<types><overloading><inheritance><subtypes>
Are there any practical use cases for subtyping primitive types?
CC BY-SA 4.0
2023-07-24T15:31:32.770Z
In some languages, such as Swift and C#, types such as `Int32` are structs and not classes, and therefore cannot be derived from.
Bbrk24
CC BY-SA 4.0
2023-07-25T00:59:57.547Z
Your question makes it sound like primitive types are not subtypes of each other. While `1` <: `1 | 42` <: `uint8` <: `uint16` <: ℕ <: ℤ, etc.. Also `false` <: `bool` in some places because API reasons.
Longinus
CC BY-SA 4.0
2023-07-25T01:14:32.247Z
"I have a function that must take an actual boolean": a subtype of booleans is an "actual boolean." It would not be a boolean otherwise. You may be conflating subtyping and subclassing?
Longinus
CC BY-SA 4.0
2023-07-25T01:22:05.447Z
@Longinus Re "a subtype of booleans is an 'actual boolean'", yes, I do know that, which is actually why I wrote "actual boolean" there.  I am ruling out passing an imitation boolean (using duck typing), which would not be an actual boolean, instead forcing the caller to provide a value whose type is a subtype with overridden behavior if they don't want certain supertype behavior.
Wheelwright
CC BY-SA 4.0
2023-07-25T02:57:32.297Z
@Longinus Oh, I missed your first comment.  I have tried to clarify that I am only talking about users of the language creating new subtypes, but your point about refinement types still stands.  I won't change the question now, since that would, for example, invalidate your answer, and anyway I had gotten myopic thinking about behavioral differences and forgotten about other uses of subtyping that I should have considered—your comment and answer helped me in that regard; thanks!
Wheelwright
CC BY-SA 4.0
2023-07-25T03:26:01.010Z
@wheelwright Yeah I guess I only responded to the `are there concrete practical situations where a user of the language not being able to subtype one of the usual primitive types is a serious problem` question, and completely forgot about the "should I extend `Bool`" one. Sorry. That said, `sealed` classes are a thing in OOP that would address this API design-wise. I'll add to the answer maybe.
Longinus
CC BY-SA 4.0
2023-07-25T12:33:12.053Z
Python doesn't have primitive types (in the sense that its types are not just thin wrappers around whatever the machine provides; in particular, a Python `int` is an arbitrary precision value, and its CPython implementation is as an array of integers representing the base-2**31 digits of the value).
chepner
CC BY-SA 4.0
2023-07-25T13:17:22.217Z
I suggest changing the title from "subtyping primitive types" to "subclassing primitive  types" or "inheriting from primitive types." When I initially read the phrase "subtyping primitive types," I thought it was obvious that the phrase meant creating types referring to actual _subsets_ of primitive types. (Hypothetical examples of such subtypes may be "integer in the range from 0 to 53, inclusive" or "string containing only lowercase ASCII letters.") So in my opinion, the current title is very misleading.
Sophie Swett
CC BY-SA 4.0
2023-07-25T13:59:24.253Z
Example in the Python standard library: [`enum.IntEnum`](https://docs.python.org/3/library/enum.html#enum.IntEnum) is subclass of `int`. Similarly `enum.StrEnum` is a subclass of `str`. Before it was added the recommended way was to use multiple inheritance from `enum.Enum` and `int` or `str`.
pabouk - Ukraine stay strong
CC BY-SA 4.0
2023-07-25T17:09:58.037Z
@TannerSwett Subtyping and Subclassing both exist and are distinct in Python.  Unless I am misinterpreting the question they do mean subtyping, not subclassing.
Chuu
CC BY-SA 4.0
2023-07-25T18:50:59.330Z
@Chuu The statement `class Doublable(int)` is subclassing, so I don't understand what makes you say that the question is about subtyping and not subclassing.
Sophie Swett
CC BY-SA 4.0
2023-07-25T20:59:36.383Z
Note that Python's standard `bool` type is a subtype of `int`.
dan04
CC BY-SA 4.0
2023-07-27T14:05:07.350Z
In Ada, you can subtype primitives. That can be surprisingly useful for type safety: "[The primary cause of this discrepancy was that one piece of ground software supplied by Lockheed Martin produced results in a United States customary unit, contrary to its Software Interface Specification (SIS), while a second system, supplied by NASA, expected those results to be in SI units](https://en.wikipedia.org/wiki/Mars_Climate_Orbiter)". There are many use cases where simple numbers have different semantics. I'm working with railway software and you'd be surprised how many "speeds" there are!
Peter - Reinstate Monica
CC BY-SA 4.0
2023-07-27T14:13:34.577Z
In other words, making types logically distinct even though they are represented the same -- without overhead! -- makes a language more expressive. It is a tool in the programmer's portfolio to [express something in code](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) instead of in a comment or documentation.
Peter - Reinstate Monica
CC BY-SA 4.0
2023-07-27T18:18:24.543Z
Finally found a question here I want to answer and it's protected. :( I really want to talk about tagged dates and numbers.
Joshua
<h1>Everything is an object</h1>
<p>Python doesn't distinguish between primitive and object types the same way that C++ or Java does. Everything, including integers, is an object. Therefore, there's no reason why the integer type could not have a subtype.</p>

CC BY-SA 4.0
2023-07-25T07:06:59.300Z
2023-07-25T07:06:59.300Z
xigoi
CC BY-SA 4.0
2023-07-25T08:02:01.443Z
I’m not one of the downvoters, but I don’t think keywords are objects.
The Empty String Photographer
CC BY-SA 4.0
2023-07-25T08:51:51.587Z
@PlaceReporter99 Where in my answer did I claim that keywords are objects?
xigoi
CC BY-SA 4.0
2023-07-25T12:58:33.367Z
You said ***EVERYTHING*** is an object. And “everything” includes keywords.
The Empty String Photographer
CC BY-SA 4.0
2023-07-25T13:34:37.403Z
More precisely, every value is a reference to an object.
kaya3
CC BY-SA 4.0
2023-07-25T16:05:37.293Z
@PlaceReporter99 That might be a wee bit of a pedantic take on the answer.
Wayne Conrad
CC BY-SA 4.0
2023-07-25T22:34:12.147Z
@PlaceReporter99 "I don’t think keywords are objects." Hold my Lisp macro while I make all keywords objects.
Longinus
<p>Two general use cases not specific to any particular language:</p>
<p>You can introduce literal types as subtypes of their respective bases.</p>
<p>Also, you can have enum types be regular subtypes of their respective base integer types.</p>

CC BY-SA 4.0
2023-07-24T20:53:37.847Z
2023-07-24T20:53:37.847Z
feldentm
CC BY-SA 4.0
2023-07-24T20:56:06.850Z
What do you mean by literal types being subtypes of their respective bases?
mousetail 'he-him'
CC BY-SA 4.0
2023-07-25T12:14:05.647Z
@mousetail: My guess is they are talking about something like TypeScript's or Scala's literal types where e.g. `1` has a singleton type of which `1` is the sole instance and which is a subtype of `int`. In Scala, every object has a singleton type, not just literals. In TypeScript, every literal has a literal type.
Jörg W Mittag
CC BY-SA 4.0
2023-07-26T18:13:19.390Z
Yes, that's the intended direction.
feldentm
<h2>Math performance ...</h2>
<p>Numbers are usually primitive types, and some languages go to extreme lengths to make them as fast as possible, mapping some types not as heap objects, but as byte payloads that exist only on stack or as fields of other objects.</p>
<p>Subtyping types with no extra overhead is a way to tag these numbers with unit dimensions, and so, to <a href="https://github.com/boostorg/units" rel="nofollow noreferrer">provide dimension checking in performance-critical code</a>.</p>
<h2>... and less explosions in space</h2>
<p>Scientific computing uses a lot of math, in performance-critical scenarios. In special, spatial scientific computing (pun intended).</p>
<p>Languages with appropriate primitive subtyping may result in less space missions ending in <a href="https://www.simscale.com/blog/nasa-mars-climate-orbiter-metric/#:%7E:text=In%20September%20of%201999%2C%20after,%2C%20i.e.%2C%20the%20metric%20units!" rel="nofollow noreferrer">explosions</a>.</p>

CC BY-SA 4.0
2023-07-26T02:13:03.877Z
2023-07-26T12:38:58.500Z
2023-07-26T12:38:58.500Z
André L F S Bacci
<p>Subtyping of value types (whether primitive or otherwise) could be very useful in contexts involving C#/.NET-style generics.  In general, references to objects are covariant (e.g. a reference to a Cat <em>is</em> also a reference to an Animal), but containers for objects are invariant (e.g. a container of Cat cannot accept Animal, and one cannot expect to read a Cat from a container of Animal).</p>
<p>On the other hand, it would be useful to allow definition of generic functions that can perform a limited range of operations on containers of Animal or subtypes thereof, including Cat, as well as functions that can perform a different limited range of operations on containers of Cat or supertypes thereof.  Methods acting upon <code>this</code> would need to be virtual and generic, and adding virtual methods to a parent class should generally be viewed as a breaking change, but if base classes are well established before subtypes are designed, that shouldn't be a problem.</p>

CC BY-SA 4.0
2023-07-25T17:17:12.787Z
2023-07-25T20:02:55.310Z
2023-07-25T20:02:55.310Z
supercat
<p><strong>Measurement units: meters, seconds, dollars, the like.</strong></p>
<p>It may be useful to know that two values are not logically compatible, even if they binary representations are. For example, meters and seconds can be both stored in a <code>float</code>, but adding or comparing them is most often pointless. From the other side, degrees can be added to radians in general, but the value containing degrees must be converted into radians first.</p>
<p>After deriving a physical formula, physicists routinely check in which units there is the result. If the result is expressed in some unexpected units, there is an error somewhere in the solution. For physical, geometrical and similar tasks it would be great to have a compiler capable of these checks at the compilation time.</p>
<p>I sometimes do this with typedef for clarity, but this is more to assist myself and not very useful as there are no compilation time checks:</p>
<pre><code>typedef float meters;
typedef float killometers;

meters a = 1;
killometers b  = a; // this could be better an error.
</code></pre>
<p>P.S. As correctly addressed in the comment, while adding units always results the same units, multiplying or dividing them creates new units: meters / seconds = m/s, velocity, so this is not that trivial but potentially more interesting and powerful. There is a <a href="https://github.com/mpusz/mp-units" rel="nofollow noreferrer">C++ 20 project</a> (seems really using C++20 features heavily) to support the units, with expressions like</p>
<pre><code>using namespace mp_units::si::unit_symbols;

10 * km / 2 == 5 * km; 
1 * km / (1 * s) == 1000 * (m / s));
</code></pre>
<p>and also</p>
<pre><code>inline constexpr auto kmph = km / h;
2 * kmph * (2 * h) == 4 * km
</code></pre>
<p>possible to check at the compilation time</p>
<p><a href="https://support.ptc.com/help/mathcad/r9.0/en/index.html#page/PTC_Mathcad_Help/unit_checking.html#wwID0EQPVGB" rel="nofollow noreferrer">Mathcad</a> provides the unit support. I am not sure if it could be called a graphical programming language or it is something different.</p>

CC BY-SA 4.0
2023-07-28T17:23:19.763Z
2023-07-29T16:44:25.123Z
2023-07-29T16:44:25.123Z
Audrius Meškauskas
CC BY-SA 4.0
2023-07-28T17:50:45.293Z
The problem with this using subtypes is that, according to the Liskov substitution principle, `a + b` cannot be an error because they are both floats, even if `a` is of type `meters` and `b` is of type `kilometers`, because you are allowed to add floats to get a float. It also makes several silent choices about the behaviour of the type, which will not be suitable for all use-cases; for example, `a + a` should be of type `meters` but `a * a` should not be, and a different type like `celsius` should not have `a + a` be of type `celsius`.
kaya3
CC BY-SA 4.0
2023-07-29T16:28:32.880Z
@kaya3 agree, as we know from physics, multiplying or dividing creates value expressed in a different another units than operands. I found all C++20 project to support the units on GitHub.
Audrius Meškauskas
<h2>Refinement Types</h2>
<p>A Refinement Type is a subtype of some type, whose inhabitants can be described by a predicate.<br />
It looks like this: <code>Even = { n ∈ ℤ | n mod 2 = 0 }</code>.<br />
Such a type is exactly what it looks like it is: it is the type of all even integrals, meaning it is a subtype of integers.</p>
<p>Is the user entering some positive integer capped at some constant in the online form?<br />
Answer: <code>{ n ∈ ℕ⁺ | n ⩽ MAX }</code>.</p>
<p>As subtyping is merely a substitution rule, for all expected integrals, such a type can be substituted with all expectations met.</p>
<h2>Singleton Types</h2>
<p>Sometimes you want to restrict some variable to a single possible value.<br />
That's a Singleton Type.</p>
<p>Some reasons of why you would do that include:</p>
<ul>
<li>API designer decided to return some T on success, and <code>false</code> on failure. Now the type is <code>T | false</code>, where <code>false</code> is a subtype of <code>bool</code>.<br />
This is common in feature-poor ecosystems like early PHP and JS libraries.</li>
<li>Language says references cannot be <code>null</code>; instead there is a distinct primitive <code>null</code> type which you must use in a union (often with some <code>T?</code> syntax sugar to mean <code>T | typeof(null)</code>). Not a subtype of anything but is starting to get common in languages with simple typesystems.</li>
<li>Field-based discrimination of united records, where you have a union of two records differing by a field.<br />
Eg. this TypeScript code: <code>{ tag: &quot;Ok&quot;, payload: String } | { tag: &quot;Err&quot;, errCode: Int }</code>.</li>
</ul>
<p>In fact, singleton string types are a really common feature, used by languages such as Erlang, Elixir, Ruby, and many Lisps, usually under names such as &quot;tags,&quot; &quot;symbols,&quot; or &quot;atoms.&quot;</p>
<p>At all times, such a singleton type, or a union thereof as seen above, can be passed to a function expecting the supertype.</p>
<h2>Dependent Types</h2>
<p>Sometimes I know the specific values that will inhabit a type.<br />
Sometimes I don't.<br />
All I know is that it is a subtype of <em>something</em>.</p>
<p>Here are Dependent Refinement Types subtyping integrals again, sans constants:</p>
<pre class="lang-hs prettyprint-override"><code>Index (xs: List a) = { i ∈ ℕ₀ | i &lt; length xs }
InclusiveRange (xs: List a) = (Index xs, Index xs)
</code></pre>
<p>Here, <code>xs</code> is just some variable. And both times, those types meet all expectations of their respective supertypes.</p>
<h2>Storage-Based Types</h2>
<p>In many, <strong>many</strong>, languages, some types are defined by their storage.<br />
In C, exists a relation <code>sizeof(signed char) ⩽ sizeof(short int) ⩽ sizeof(int) ⩽ sizeof(long int)</code>, and from it is derived the subtyping <code>signed char &lt;: short int &lt;: int &lt;: long int</code>.</p>
<p>In other languages with well-specified bit-storage, it is even more obvious: <code>u8 &lt;: u16 &lt;: u32 &lt;: u64 &lt;: u128</code> etc...</p>
<h2>Relative Pointer Types</h2>
<p>Remember that TypeScript union?<br />
Well sometimes, you're writing in C, where you don't have the luxury of &quot;union of record with comparable fields types,&quot; and all you have is a pointer that points to some place of interest.</p>
<p>Take this fairly common idiom:</p>
<pre class="lang-c prettyprint-override"><code>enum tag { USR, SYS };
struct msg_t
///`msg_t*` should point only to instances of `usr_msg_t`/`sys_msg_t`'s `base` field.
{
    enum tag tag;
    uint64_t timestamp;
};
struct usr_msg_t
{
    struct msg_t base;
    string_t name;
    void* args;
};
struct sys_msg_t
{
    struct msg_t base;
    enum sys_msg_kind_t whatever;
};

const struct msg_t* new_usr_msg(string_t name, void* args)
{
    struct usr_msg_t* msg = malloc_chk(sizeof(struct usr_msg_t));
    *msg = (struct usr_msg_t){
        .base = { .tag = USR, .timestamp = time() },
        .name = name,
        .args = args
    };
    return &amp;msg-&gt;base;
}
const struct msg_t* new_sys_msg(sys_msg_kind_t msgk)
{
    struct sys_msg_t* msg = malloc_chk(sizeof(sys_msg_t));
    *msg = (struct sys_msg_t){
        .base = { .tag = SYS, .timestamp = time() },
        .whatever = msgk
    };
    return &amp;msg-&gt;base;
}

#define get_parent_of_ptr(ptr, m, t) (t*)(ptr - offsetof(t, m))
void disp_msg(const struct msg_t* msg)
{
    switch(msg-&gt;tag) {
    case USR:
        const auto msg_ = get_parent_of_ptr(msg, base, struct usr_msg_t);
        f(msg_-&gt;name, msg_-&gt;args);
        break;
    case SYS:
        const auto msg_ = get_parent_of_ptr(msg, base, struct sys_msg_t);
        g(msg_-&gt;whatever);
        //...
    }
}
</code></pre>
<p>In <code>disp_msg</code>'s signature, what is <code>msg</code>'s type?<br />
C says it is <code>const struct msg_t*</code>, <em>but we can do better</em>.<br />
In this struct subtyping idiom, the pointer we receive points to some tag (and associated data) that is shared across multiple subtypes and that can be used to discriminate them.<br />
The <code>get_parent_of_ptr</code> operation doing pointer arithmetic clearly demonstrates this property.<br />
So while C does not itself understand it, you, as a programmer, are supposed to understand that <code>usr_msg_t</code> and <code>sys_msg_t</code> are subtypes of <code>msg_t</code>.</p>
<p>And as language designers, we can do better!<br />
This parameter is not just a random pointer: it is a <em>relative</em> pointer! Precisely, its type is <code>(usr_msg_t § base)* | (sys_msg_t § base)*</code>, which happens to be a subtype of <code>msg_t*</code>.</p>
<p>This is also a demonstration of where typing discipline clashes with programming languages.<br />
We're talking of values of types which the language we use does not understand, forcing us to rely on manual dynamic code (including manual type checks, and in the case of a language like C, pointer arithmetic too), and on <strong>documentation in natural languages</strong>, none of which are actually checked by a compiler!</p>
<h2>We Can Do Better</h2>
<p>As I hope I demonstrated, typing discipline is a greater topic than just <em>language features</em>.<br />
When designing interfaces, we are more often than not talking of types which the used language <em>cannot</em> express, so we rely on Human-oriented, ignored-by-compilers, documentation.</p>
<p>The API's code says the <code>getUserRelations</code> function returns an <code>int</code>, but the API's documentation contradicts this by saying it is an <code>{ n ∈ int | n &lt; 100 }</code> - a subtype of <code>int</code>.</p>
<p>That one illustration of subtypes as output may be harmless, but what about subtypes <em>as input</em>?<br />
The API's code says the <code>send_remote_msg</code> function takes in a <code>usr_msg_t*</code>, and the API's documentation says it is <code>(sys_msg_t § base)* | (usr_msg_t § base)*</code>.<br />
What happens if I pass in some other message pointer? What if I pass in <code>NULL</code>?<br />
The documentation is clear in that I must not, but the code is way more liberal, and the compiler will never complain!</p>
<p>So indeed: subtypes of &quot;primitive types&quot; are in everything, everywhere, all at once.</p>

CC BY-SA 4.0
2023-07-25T02:31:35.897Z
2023-07-27T17:59:34.467Z
2023-07-27T17:59:34.467Z
Longinus
<p>User-defined subtypes of primitive types are a thorny issue, because primitive types have many operations which return new values of the primitive type. For example, if you add two of your <code>Doublable</code> objects in Python, you don't get a <code>Doublable</code>, you just get an <code>int</code>.</p>
<p>This will be annoying in many use-cases but desirable in others. For example, if you have a subclass of <code>float</code> named <code>TimeDelta</code> which represents a length of time in seconds, then adding two <code>TimeDelta</code> objects should logically result in a new <code>TimeDelta</code> object. On the other hand, if you have a subclass of <code>str</code> named <code>EmailAddress</code>, then concatenation should not produce a new <code>EmailAddress</code> object.</p>
<p>Another problem is the <a href="https://en.wikipedia.org/wiki/Liskov_substitution_principle" rel="noreferrer">Liskov substitution principle</a>. If a <code>TimeDelta</code> and a <code>Temperature</code> are both <code>float</code>s then it must be possible to add them to get a <code>float</code> (or some subtype of <code>float</code>, of course). But any program which attempts to add a time (measured in seconds) to a temperature (measured in degrees) is necessarily doing something wrong, and the &quot;correct&quot; behaviour for such a program should be that it fails, preferably at compile-time.</p>
<hr />
<p>I would say therefore that allowing user-defined subtypes of primitives is just a bad idea, because it means the user's type inherits a bunch of behaviours from the primitive type, many of which will often be incorrect for the actual use-case ─ and it <em>doesn't</em> make the user think about this. This way lies bugs.</p>
<p>A safer option is to allow the user to define a regular class or struct with a field for the primitive value, and use operator-overloading to explicitly declare what operations they want with what results they want. For example, <code>__add__</code> on <code>TimeDelta</code> should check that the other operand is a <code>TimeDelta</code> and return a new <code>TimeDelta</code>, but <code>__add__</code> on  an <code>EmailAddress</code> should return <code>str</code>.</p>
<p>This does mean that the user will have to write a lot of &quot;boilerplate&quot; code, but I think it is justified ─ that &quot;boilerplate&quot; code does involve a lot of use-case-specific decisions, including what operations it makes sense to support, whether to check that the other operand is also of the same type, and whether to return an instance of the same type.</p>

CC BY-SA 4.0
2023-07-24T18:11:46.877Z
2023-07-24T18:11:46.877Z
kaya3
CC BY-SA 4.0
2023-07-25T12:39:13.540Z
Adding two temperatures is also nonsense - obviously since adding Kelvin and adding Celsius gives incompatible results. And "seconds since start of epoch" shouldn't be added either.
gnasher729
CC BY-SA 4.0
2023-07-25T13:32:42.687Z
@gnasher729 Indeed, I just tried to keep the examples simple.
kaya3
CC BY-SA 4.0
2023-07-27T00:01:11.600Z
@gnasher729 even adding just thwo celsius-temperatures doesn't really make sense.
Paŭlo Ebermann
CC BY-SA 4.0
2023-07-29T23:15:57.050Z
@PaŭloEbermann Averaging temperatures requires adding then first.
prosfilaes
CC BY-SA 4.0
2023-07-30T10:25:44.537Z
@prosfilaes Sure, but in terms of types/units, the sum of two temperatures in Celsius is not a temperature in Celsius ─ and neither is half of a temperature in Celsius. You could hypothetically define the sum as a temperature in "double-Celsius" and then half of a double-Celsius temperature is a Celsius temperature, I suppose, but this gets complicated very quickly once you have the average of a variable number of temperatures.
kaya3
CC BY-SA 4.0
2023-11-01T20:28:14.287Z
@prosfilaes: It's possible to average things of an "absolute" type (e.g. temperatures) that can't be added to other things of absolute type, but can be subtracted to other things of absolute type to yield something of a "delta" type, and which can be added to values of a "delta" type to yield another thing of "absolute" type.  The average of "absolute" values `x` and `y` in such case wouldn't be `(x+y)/2`, since `x` and `y` can't be added, but rather `x+(y-x)/2`.
supercat
CC BY-SA 4.0
2023-11-01T20:30:21.727Z
@kaya3: If one recognizes a "delta temperature" type, one can take the average of a multiple temperatures by selecting one as base, computing the difference between that and each temperature in the set (one of the differences will be zero), adding those together and multiplying by the reciprocal of the number of elements, and adding that to the original element that was chosen.
supercat
<p>Yes.  One application of subtyping is to produce an object that behaves the same way as another object, but records the operations performed on it.  This recording can be used in a number of ways, e.g. for debugging or logging, or to allow the operation to be repeated or reversed later.</p>
<p>A specific application of doing this to a numeric type is to record the operations performed in a calculation in order to automatically differentiate it later.  An example where this is done is the Python machine learning library PyTorch (although it records the operations on tensors rather than a built-in type, it could equally do the same thing with built-in types were there a necessity for this).</p>

CC BY-SA 4.0
2023-07-24T15:57:14.070Z
2023-07-24T15:57:14.070Z
occipita
CC BY-SA 4.0
2023-07-24T18:21:20.563Z
but then you may as well use a decorator instead of a subtype, which has the additional advantage that you get a runtime error if you forgot one?
Criticize SE actions means ban
CC BY-SA 4.0
2023-07-25T11:22:56.650Z
In a "proper" OO language that passes the "Ingalls Test", this would be true, but unfortunately, many modern OO languages fail the test. The Ingalls Test is (roughly): can you write your own integer class, create 4 instances of it and pass them to the language's builtin graphics system to open a window at a specific position? If no, then the language is not object-oriented. Java, for example, fails this test: I can't write a class that behaves indistinguishably from `int`. In Ruby, you still can't create a class that is indistinguishable from `Integer`, but at least, most methods that require
Jörg W Mittag
CC BY-SA 4.0
2023-07-25T11:23:46.643Z
… an `Integer` will accept anything that responds to `to_int` in its stead. However, something similar does not exist for booleans: It is impossible to create an object that behaves like `false`.
Jörg W Mittag
<h2>Marking encodings (safe vs. unsafe strings)</h2>
<p>Like Occipita, I only really see a use for strings.</p>
<p>In Django and Jinja, there is a subtype of string called &quot;SafeString&quot;. It has no actual custom functionality, but when embedded in HTML, it is not escaped:</p>
<pre class="lang-py prettyprint-override"><code>string_1 = &quot;&lt;h1&gt;&quot;
string_2 = safe(&quot;&lt;h1&gt;&quot;)

jinja.render_string(&quot;{{ a }} {{ b }}&quot;, a=string_1, b=string_2)
# output: &amp;lt;h1&amp;gt;&lt;h1&gt;
</code></pre>
<p>The advantage of subtyping is you always get all the default operations on strings. If you used a wrapper type instead, like is the convention in many other languages, you still need to implement concatenation, indexing, and all other options manually on top of your wrapper type.</p>

CC BY-SA 4.0
2023-07-24T16:11:49.013Z
2023-07-24T16:11:49.013Z
mousetail 'he-him'
CC BY-SA 4.0
2023-07-25T14:42:08.770Z
Only for strings? What about e.g. `NonNegativeInt`? It's the same thing: we flow that value around the program but if we lose the type information in the process we end up checking and re-checking the boundary condition in multiple places.
Jared Smith
<p>I think you should first ask whether there are practical use cases for subclassing <em>at all</em>.</p>
<p>There is nothing doable with subclassing that can't be done with containment and delegation (and interfaces or duck typing for polymorphism). The main advantage of subclassing over C&amp;D seems to be convenience: you don't have a write a bunch of forwarding functions. But you pay for that with a significant disadvantage, the fragile base class problem.</p>
<p>All of that seems largely independent of whether the class you're inheriting from is primitive or not. In fact I'm not sure what primitive means in this context. I think that types that ship with the language shouldn't be imbued with special magic without a good reason. Performance seems like a good reason, but this doesn't.</p>

CC BY-SA 4.0
2023-07-24T19:34:00.470Z
2023-07-24T19:34:00.470Z
benrg
CC BY-SA 4.0
2023-07-26T20:27:18.897Z
*There is nothing doable with subclassing that can't be done with containment and delegation (and interfaces or duck typing for polymorphism)* -- subclassing is in fact exactly equivalent to interface subtyping plus delegation to an instance of the superclass, but is of course much more convenient to implement.
occipita
CC BY-SA 4.0
2023-07-31T05:10:07.127Z
one language of note that somewhat fits into this space is Julia. Julia let's you subtype but not instantiate abstract types and doesn't let you instantiate concrete types. this gives speed benefits (much less pointer chasing) and significantly reduces the need for contravariant types which are a major new programmer pain point.
Oscar Smith
CC BY-SA 4.0
2023-11-10T16:19:01.470Z
@occipita @benrq Delegation has its subtleties, namely, how to deal with `this`/`self`. Some times the object delegated-to will need to pass references to "self" (e.g. installing a callback), and you'll want that "self" to be the original delegator. Stroustrup reports (in "Design and Evolution of C++") delegation being problematic when tried/experimented/explored.
Pablo H
# Are there any practical use cases for subtyping primitive types?
Posted by: username_0 on 2023-07-24
In, for example, Python, it is possible to subtype primitive types:
>>> class Doublable(int):
...     def double(self):
...         return 2 * self
... 
>>> d = Doublable(4)
>>> d == 4
True
>>> d is 4
False
>>> d.double()
8

The same is true of other primitive types, like bool or str, and I have actually seen string subtypes in the wild.  However, that design felt off to me—if I had written the code, my inclination, despite the higher up-front effort, would have been to declare a new type with a string field and then write methods to imitate the needed string-like behavior.
In a language where forbidding subtypes is possible and where the language design is meant to encourage maintainable code (so not an esolang or golflang), are there concrete practical situations where a user of the language not being able to subtype one of the usual primitive types (the unit type, the boolean type, numeric types, character types, perhaps built-in collection types or collection-like types such as an option type…) is a serious problem?
In languages with duck typing, of course the programmer can just imitate the primitive type, as described above.  But suppose that's not an option—if, say, I have a function that must take an actual boolean, is there any reason that the "right" design would be to pass a value that subtypes boolean rather than doing some conversion to a proper boolean before the call?  (In my particular case, all primitive values are immutable, so if such concerns arise only for mutables like Python's list, that would be good to know.)
I understand that it can be hard to prove a negative; if the answer is "no", a well-reasoned heuristic argument would suffice as an answer.
**Tags:** types overloading inheritance subtypes
> **Comments:**
> **username_1**: In some languages, such as Swift and C#, types such as `Int32` are structs and not classes, and therefore cannot be derived from.  
> **username_2**: Your question makes it sound like primitive types are not subtypes of each other. While `1` <: `1 | 42` <: `uint8` <: `uint16` <: ℕ <: ℤ, etc.. Also `false` <: `bool` in some places because API reasons.  
> **username_2**: "I have a function that must take an actual boolean": a subtype of booleans is an "actual boolean." It would not be a boolean otherwise. You may be conflating subtyping and subclassing?  
> **username_0**: @Longinus Re "a subtype of booleans is an 'actual boolean'", yes, I do know that, which is actually why I wrote "actual boolean" there.  I am ruling out passing an imitation boolean (using duck typing), which would not be an actual boolean, instead forcing the caller to provide a value whose type is a subtype with overridden behavior if they don't want certain supertype behavior.  
> **username_0**: @Longinus Oh, I missed your first comment.  I have tried to clarify that I am only talking about users of the language creating new subtypes, but your point about refinement types still stands.  I won't change the question now, since that would, for example, invalidate your answer, and anyway I had gotten myopic thinking about behavioral differences and forgotten about other uses of subtyping that I should have considered—your comment and answer helped me in that regard; thanks!  
> **username_2**: @wheelwright Yeah I guess I only responded to the `are there concrete practical situations where a user of the language not being able to subtype one of the usual primitive types is a serious problem` question, and completely forgot about the "should I extend `Bool`" one. Sorry. That said, `sealed` classes are a thing in OOP that would address this API design-wise. I'll add to the answer maybe.  
> **username_3**: Python doesn't have primitive types (in the sense that its types are not just thin wrappers around whatever the machine provides; in particular, a Python `int` is an arbitrary precision value, and its CPython implementation is as an array of integers representing the base-2**31 digits of the value).  
> **username_4**: I suggest changing the title from "subtyping primitive types" to "subclassing primitive  types" or "inheriting from primitive types." When I initially read the phrase "subtyping primitive types," I thought it was obvious that the phrase meant creating types referring to actual _subsets_ of primitive types. (Hypothetical examples of such subtypes may be "integer in the range from 0 to 53, inclusive" or "string containing only lowercase ASCII letters.") So in my opinion, the current title is very misleading.  
> **username_5**: Example in the Python standard library: [`enum.IntEnum`](https://docs.python.org/3/library/enum.html#enum.IntEnum) is subclass of `int`. Similarly `enum.StrEnum` is a subclass of `str`. Before it was added the recommended way was to use multiple inheritance from `enum.Enum` and `int` or `str`.  
> **username_6**: @TannerSwett Subtyping and Subclassing both exist and are distinct in Python.  Unless I am misinterpreting the question they do mean subtyping, not subclassing.  
> **username_4**: @Chuu The statement `class Doublable(int)` is subclassing, so I don't understand what makes you say that the question is about subtyping and not subclassing.  
> **username_7**: Note that Python's standard `bool` type is a subtype of `int`.  
> **username_8**: In Ada, you can subtype primitives. That can be surprisingly useful for type safety: "[The primary cause of this discrepancy was that one piece of ground software supplied by Lockheed Martin produced results in a United States customary unit, contrary to its Software Interface Specification (SIS), while a second system, supplied by NASA, expected those results to be in SI units](https://en.wikipedia.org/wiki/Mars_Climate_Orbiter)". There are many use cases where simple numbers have different semantics. I'm working with railway software and you'd be surprised how many "speeds" there are!  
> **username_8**: In other words, making types logically distinct even though they are represented the same -- without overhead! -- makes a language more expressive. It is a tool in the programmer's portfolio to [express something in code](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) instead of in a comment or documentation.  
> **username_9**: Finally found a question here I want to answer and it's protected. :( I really want to talk about tagged dates and numbers.  
---
## 10 answers
### Answer by username_23 on 2023-07-24. Score: 17
Yes. One application of subtyping is to produce an object that behaves the same way as another object, but records the operations performed on it. This recording can be used in a number of ways, e.g. for debugging or logging, or to allow the operation to be repeated or reversed later.

A specific application of doing this to a numeric type is to record the operations performed in a calculation in order to automatically differentiate it later. An example where this is done is the Python machine learning library PyTorch (although it records the operations on tensors rather than a built-in type, it could equally do the same thing with built-in types were there a necessity for this).
> **Comments:**
> **username_24** (2023-07-24): but then you may as well use a decorator instead of a subtype, which has the additional advantage that you get a runtime error if you forgot one?  
> **username_16** (2023-07-25): In a "proper" OO language that passes the "Ingalls Test", this would be true, but unfortunately, many modern OO languages fail the test. The Ingalls Test is (roughly): can you write your own integer class, create 4 instances of it and pass them to the language's builtin graphics system to open a window at a specific position? If no, then the language is not object-oriented. Java, for example, fails this test: I can't write a class that behaves indistinguishably from `int`. In Ruby, you still can't create a class that is indistinguishable from `Integer`, but at least, most methods that require  
> **username_16** (2023-07-25): … an `Integer` will accept anything that responds to `to_int` in its stead. However, something similar does not exist for booleans: It is impossible to create an object that behaves like `false`.  
---
### Answer by username_2 on 2023-07-25. Score: 13
Refinement Types
A Refinement Type is a subtype of some type, whose inhabitants can be described by a predicate.
It looks like this: Even = { n ∈ ℤ | n mod 2 = 0 }.
Such a type is exactly what it looks like it is: it is the type of all even integrals, meaning it is a subtype of integers.
Is the user entering some positive integer capped at some constant in the online form?
Answer: { n ∈ ℕ⁺ | n ⩽ MAX }.
As subtyping is merely a substitution rule, for all expected integrals, such a type can be substituted with all expectations met.
Singleton Types
Sometimes you want to restrict some variable to a single possible value.
That's a Singleton Type.
Some reasons of why you would do that include:

API designer decided to return some T on success, and false on failure. Now the type is T | false, where false is a subtype of bool.
This is common in feature-poor ecosystems like early PHP and JS libraries.
Language says references cannot be null; instead there is a distinct primitive null type which you must use in a union (often with some T? syntax sugar to mean T | typeof(null)). Not a subtype of anything but is starting to get common in languages with simple typesystems.
Field-based discrimination of united records, where you have a union of two records differing by a field.
Eg. this TypeScript code: { tag: "Ok", payload: String } | { tag: "Err", errCode: Int }.

In fact, singleton string types are a really common feature, used by languages such as Erlang, Elixir, Ruby, and many Lisps, usually under names such as "tags," "symbols," or "atoms."
At all times, such a singleton type, or a union thereof as seen above, can be passed to a function expecting the supertype.
Dependent Types
Sometimes I know the specific values that will inhabit a type.
Sometimes I don't.
All I know is that it is a subtype of something.
Here are Dependent Refinement Types subtyping integrals again, sans constants:
Index (xs: List a) = { i ∈ ℕ₀ | i < length xs }
InclusiveRange (xs: List a) = (Index xs, Index xs)

Here, xs is just some variable. And both times, those types meet all expectations of their respective supertypes.
Storage-Based Types
In many, many, languages, some types are defined by their storage.
In C, exists a relation sizeof(signed char) ⩽ sizeof(short int) ⩽ sizeof(int) ⩽ sizeof(long int), and from it is derived the subtyping signed char <: short int <: int <: long int.
In other languages with well-specified bit-storage, it is even more obvious: u8 <: u16 <: u32 <: u64 <: u128 etc...
Relative Pointer Types
Remember that TypeScript union?
Well sometimes, you're writing in C, where you don't have the luxury of "union of record with comparable fields types," and all you have is a pointer that points to some place of interest.
Take this fairly common idiom:
enum tag { USR, SYS };
struct msg_t
///`msg_t*` should point only to instances of `usr_msg_t`/`sys_msg_t`'s `base` field.
{
    enum tag tag;
    uint64_t timestamp;
};
struct usr_msg_t
{
    struct msg_t base;
    string_t name;
    void* args;
};
struct sys_msg_t
{
    struct msg_t base;
    enum sys_msg_kind_t whatever;
};

const struct msg_t* new_usr_msg(string_t name, void* args)
{
    struct usr_msg_t* msg = malloc_chk(sizeof(struct usr_msg_t));
    *msg = (struct usr_msg_t){
        .base = { .tag = USR, .timestamp = time() },
        .name = name,
        .args = args
    };
    return &msg->base;
}
const struct msg_t* new_sys_msg(sys_msg_kind_t msgk)
{
    struct sys_msg_t* msg = malloc_chk(sizeof(sys_msg_t));
    *msg = (struct sys_msg_t){
        .base = { .tag = SYS, .timestamp = time() },
        .whatever = msgk
    };
    return &msg->base;
}

#define get_parent_of_ptr(ptr, m, t) (t*)(ptr - offsetof(t, m))
void disp_msg(const struct msg_t* msg)
{
    switch(msg->tag) {
    case USR:
        const auto msg_ = get_parent_of_ptr(msg, base, struct usr_msg_t);
        f(msg_->name, msg_->args);
        break;
    case SYS:
        const auto msg_ = get_parent_of_ptr(msg, base, struct sys_msg_t);
        g(msg_->whatever);
        //...
    }
}

In disp_msg's signature, what is msg's type?
C says it is const struct msg_t*, but we can do better.
In this struct subtyping idiom, the pointer we receive points to some tag (and associated data) that is shared across multiple subtypes and that can be used to discriminate them.
The get_parent_of_ptr operation doing pointer arithmetic clearly demonstrates this property.
So while C does not itself understand it, you, as a programmer, are supposed to understand that usr_msg_t and sys_msg_t are subtypes of msg_t.
And as language designers, we can do better!
This parameter is not just a random pointer: it is a relative pointer! Precisely, its type is (usr_msg_t § base)* | (sys_msg_t § base)*, which happens to be a subtype of msg_t*.
This is also a demonstration of where typing discipline clashes with programming languages.
We're talking of values of types which the language we use does not understand, forcing us to rely on manual dynamic code (including manual type checks, and in the case of a language like C, pointer arithmetic too), and on documentation in natural languages, none of which are actually checked by a compiler!
We Can Do Better
As I hope I demonstrated, typing discipline is a greater topic than just language features.
When designing interfaces, we are more often than not talking of types which the used language cannot express, so we rely on Human-oriented, ignored-by-compilers, documentation.
The API's code says the getUserRelations function returns an int, but the API's documentation contradicts this by saying it is an { n ∈ int | n < 100 } - a subtype of int.
That one illustration of subtypes as output may be harmless, but what about subtypes as input?
The API's code says the send_remote_msg function takes in a usr_msg_t*, and the API's documentation says it is (sys_msg_t § base)* | (usr_msg_t § base)*.
What happens if I pass in some other message pointer? What if I pass in NULL?
The documentation is clear in that I must not, but the code is way more liberal, and the compiler will never complain!
So indeed: subtypes of "primitive types" are in everything, everywhere, all at once.
---
### Answer by username_12 on 2023-07-24. Score: 11
User-defined subtypes of primitive types are a thorny issue, because primitive types have many operations which return new values of the primitive type. For example, if you add two of your `Doublable` objects in Python, you don't get a `Doublable`, you just get an `int`.

This will be annoying in many use-cases but desirable in others. For example, if you have a subclass of `float` named `TimeDelta` which represents a length of time in seconds, then adding two `TimeDelta` objects should logically result in a new `TimeDelta` object. On the other hand, if you have a subclass of `str` named `EmailAddress`, then concatenation should not produce a new `EmailAddress` object.

Another problem is the [Liskov substitution principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle). If a `TimeDelta` and a `Temperature` are both `float`s then it must be possible to add them to get a `float` (or some subtype of `float`, of course). But any program which attempts to add a time (measured in seconds) to a temperature (measured in degrees) is necessarily doing something wrong, and the "correct" behaviour for such a program should be that it fails, preferably at compile-time.

---

I would say therefore that allowing user-defined subtypes of primitives is just a bad idea, because it means the user's type inherits a bunch of behaviours from the primitive type, many of which will often be incorrect for the actual use-case ─ and it *doesn't* make the user think about this. This way lies bugs.

A safer option is to allow the user to define a regular class or struct with a field for the primitive value, and use operator-overloading to explicitly declare what operations they want with what results they want. For example, `__add__` on `TimeDelta` should check that the other operand is a `TimeDelta` and return a new `TimeDelta`, but `__add__` on an `EmailAddress` should return `str`.

This does mean that the user will have to write a lot of "boilerplate" code, but I think it is justified ─ that "boilerplate" code does involve a lot of use-case-specific decisions, including what operations it makes sense to support, whether to check that the other operand is also of the same type, and whether to return an instance of the same type.
> **Comments:**
> **username_20** (2023-07-25): Adding two temperatures is also nonsense - obviously since adding Kelvin and adding Celsius gives incompatible results. And "seconds since start of epoch" shouldn't be added either.  
> **username_12** (2023-07-25): @gnasher729 Indeed, I just tried to keep the examples simple.  
> **username_21** (2023-07-27): @gnasher729 even adding just thwo celsius-temperatures doesn't really make sense.  
> **username_22** (2023-07-29): @PaŭloEbermann Averaging temperatures requires adding then first.  
> **username_12** (2023-07-30): @prosfilaes Sure, but in terms of types/units, the sum of two temperatures in Celsius is not a temperature in Celsius ─ and neither is half of a temperature in Celsius. You could hypothetically define the sum as a temperature in "double-Celsius" and then half of a double-Celsius temperature is a Celsius temperature, I suppose, but this gets complicated very quickly once you have the average of a variable number of temperatures.  
> **username_18** (2023-11-01): @prosfilaes: It's possible to average things of an "absolute" type (e.g. temperatures) that can't be added to other things of absolute type, but can be subtracted to other things of absolute type to yield something of a "delta" type, and which can be added to values of a "delta" type to yield another thing of "absolute" type.  The average of "absolute" values `x` and `y` in such case wouldn't be `(x+y)/2`, since `x` and `y` can't be added, but rather `x+(y-x)/2`.  
> **username_18** (2023-11-01): @kaya3: If one recognizes a "delta temperature" type, one can take the average of a multiple temperatures by selecting one as base, computing the difference between that and each temperature in the set (one of the differences will be zero), adding those together and multiplying by the reciprocal of the number of elements, and adding that to the original element that was chosen.  
---
### Answer by username_15 on 2023-07-24. Score: 5
Marking encodings (safe vs. unsafe strings)
Like Occipita, I only really see a use for strings.
In Django and Jinja, there is a subtype of string called "SafeString". It has no actual custom functionality, but when embedded in HTML, it is not escaped:
string_1 = ""
string_2 = safe("")

jinja.render_string("{{ a }} {{ b }}", a=string_1, b=string_2)
# output: <h1>

The advantage of subtyping is you always get all the default operations on strings. If you used a wrapper type instead, like is the convention in many other languages, you still need to implement concatenation, indexing, and all other options manually on top of your wrapper type.
> **Comments:**
> **username_25** (2023-07-25): Only for strings? What about e.g. `NonNegativeInt`? It's the same thing: we flow that value around the program but if we lose the type information in the process we end up checking and re-checking the boundary condition in multiple places.  
---
### Answer by username_26 on 2023-07-24. Score: 4
I think you should first ask whether there are practical use cases for subclassing *at all*.

There is nothing doable with subclassing that can't be done with containment and delegation (and interfaces or duck typing for polymorphism). The main advantage of subclassing over C&D seems to be convenience: you don't have a write a bunch of forwarding functions. But you pay for that with a significant disadvantage, the fragile base class problem.

All of that seems largely independent of whether the class you're inheriting from is primitive or not. In fact I'm not sure what primitive means in this context. I think that types that ship with the language shouldn't be imbued with special magic without a good reason. Performance seems like a good reason, but this doesn't.
> **Comments:**
> **username_23** (2023-07-26): *There is nothing doable with subclassing that can't be done with containment and delegation (and interfaces or duck typing for polymorphism)* -- subclassing is in fact exactly equivalent to interface subtyping plus delegation to an instance of the superclass, but is of course much more convenient to implement.  
> **username_27** (2023-07-31): one language of note that somewhat fits into this space is Julia. Julia let's you subtype but not instantiate abstract types and doesn't let you instantiate concrete types. this gives speed benefits (much less pointer chasing) and significantly reduces the need for contravariant types which are a major new programmer pain point.  
> **username_28** (2023-11-10): @occipita @benrq Delegation has its subtleties, namely, how to deal with `this`/`self`. Some times the object delegated-to will need to pass references to "self" (e.g. installing a callback), and you'll want that "self" to be the original delegator. Stroustrup reports (in "Design and Evolution of C++") delegation being problematic when tried/experimented/explored.  
---
### Answer by username_10 on 2023-07-25. Score: 2
# Everything is an object

Python doesn't distinguish between primitive and object types the same way that C++ or Java does. Everything, including integers, is an object. Therefore, there's no reason why the integer type could not have a subtype.
> **Comments:**
> **username_11** (2023-07-25): I’m not one of the downvoters, but I don’t think keywords are objects.  
> **username_10** (2023-07-25): @PlaceReporter99 Where in my answer did I claim that keywords are objects?  
> **username_11** (2023-07-25): You said ***EVERYTHING*** is an object. And “everything” includes keywords.  
> **username_12** (2023-07-25): More precisely, every value is a reference to an object.  
> **username_13** (2023-07-25): @PlaceReporter99 That might be a wee bit of a pedantic take on the answer.  
> **username_2** (2023-07-25): @PlaceReporter99 "I don’t think keywords are objects." Hold my Lisp macro while I make all keywords objects.  
---
### Answer by username_14 on 2023-07-24. Score: 2
Two general use cases not specific to any particular language:

You can introduce literal types as subtypes of their respective bases.

Also, you can have enum types be regular subtypes of their respective base integer types.
> **Comments:**
> **username_15** (2023-07-24): What do you mean by literal types being subtypes of their respective bases?  
> **username_16** (2023-07-25): @mousetail: My guess is they are talking about something like TypeScript's or Scala's literal types where e.g. `1` has a singleton type of which `1` is the sole instance and which is a subtype of `int`. In Scala, every object has a singleton type, not just literals. In TypeScript, every literal has a literal type.  
> **username_14** (2023-07-26): Yes, that's the intended direction.  
---
### Answer by username_17 on 2023-07-26. Score: 2
## Math performance ...

Numbers are usually primitive types, and some languages go to extreme lengths to make them as fast as possible, mapping some types not as heap objects, but as byte payloads that exist only on stack or as fields of other objects.

Subtyping types with no extra overhead is a way to tag these numbers with unit dimensions, and so, to [provide dimension checking in performance-critical code](https://github.com/boostorg/units).

## ... and less explosions in space

Scientific computing uses a lot of math, in performance-critical scenarios. In special, spatial scientific computing (pun intended).

Languages with appropriate primitive subtyping may result in less space missions ending in [explosions](https://www.simscale.com/blog/nasa-mars-climate-orbiter-metric/#:%7E:text=In%20September%20of%201999%2C%20after,%2C%20i.e.%2C%20the%20metric%20units!).
---
### Answer by username_18 on 2023-07-25. Score: 1
Subtyping of value types (whether primitive or otherwise) could be very useful in contexts involving C#/.NET-style generics. In general, references to objects are covariant (e.g. a reference to a Cat *is* also a reference to an Animal), but containers for objects are invariant (e.g. a container of Cat cannot accept Animal, and one cannot expect to read a Cat from a container of Animal).

On the other hand, it would be useful to allow definition of generic functions that can perform a limited range of operations on containers of Animal or subtypes thereof, including Cat, as well as functions that can perform a different limited range of operations on containers of Cat or supertypes thereof. Methods acting upon `this` would need to be virtual and generic, and adding virtual methods to a parent class should generally be viewed as a breaking change, but if base classes are well established before subtypes are designed, that shouldn't be a problem.
---
### Answer by username_19 on 2023-07-28. Score: 1
Measurement units: meters, seconds, dollars, the like.
It may be useful to know that two values are not logically compatible, even if they binary representations are. For example, meters and seconds can be both stored in a float, but adding or comparing them is most often pointless. From the other side, degrees can be added to radians in general, but the value containing degrees must be converted into radians first.
After deriving a physical formula, physicists routinely check in which units there is the result. If the result is expressed in some unexpected units, there is an error somewhere in the solution. For physical, geometrical and similar tasks it would be great to have a compiler capable of these checks at the compilation time.
I sometimes do this with typedef for clarity, but this is more to assist myself and not very useful as there are no compilation time checks:
typedef float meters;
typedef float killometers;

meters a = 1;
killometers b  = a; // this could be better an error.

P.S. As correctly addressed in the comment, while adding units always results the same units, multiplying or dividing them creates new units: meters / seconds = m/s, velocity, so this is not that trivial but potentially more interesting and powerful. There is a C++ 20 project (seems really using C++20 features heavily) to support the units, with expressions like
using namespace mp_units::si::unit_symbols;

10 * km / 2 == 5 * km; 
1 * km / (1 * s) == 1000 * (m / s));

and also
inline constexpr auto kmph = km / h;
2 * kmph * (2 * h) == 4 * km

possible to check at the compilation time
Mathcad provides the unit support. I am not sure if it could be called a graphical programming language or it is something different.
> **Comments:**
> **username_12** (2023-07-28): The problem with this using subtypes is that, according to the Liskov substitution principle, `a + b` cannot be an error because they are both floats, even if `a` is of type `meters` and `b` is of type `kilometers`, because you are allowed to add floats to get a float. It also makes several silent choices about the behaviour of the type, which will not be suitable for all use-cases; for example, `a + a` should be of type `meters` but `a * a` should not be, and a different type like `celsius` should not have `a + a` be of type `celsius`.  
> **username_19** (2023-07-29): @kaya3 agree, as we know from physics, multiplying or dividing creates value expressed in a different another units than operands. I found all C++20 project to support the units on GitHub.  
---
