<p>An integer range type like <code>0..100</code> means that a value can be any integer in that range. Arithmetic operations on range types produce other range types, for example if <code>a: 10..20</code> and <code>b: 5..7</code> then <code>a + b: 15..27</code>. (Each range here includes both endpoints, for the sake of the example.) To be clear, here <code>0..100</code> is a type, not a value; the values are integers in that range.</p>
<p>Checking if a number is in a range can be treated as a fallible type conversion, e.g. converting <code>x</code> to type <code>0..100</code> could return an optional value which is <code>None</code> if <code>x</code> is not in that range. Alternatively, it can be treated as a type predicate, so that <code>if(x in 0..100) { ... }</code> narrows the type of <code>x</code> within the conditional branch.</p>
<p>Range types are used in a few languages, notably <a href="https://en.wikipedia.org/wiki/Ada_(programming_language)#Data_types" rel="noreferrer">Ada</a> (which <em>only</em> has integer types with defined ranges). They have some benefits, such as eliding bounds-checking for arrays of known length, avoiding overflows by using wide enough integers for any operation, and preventing division-by-zero errors. They also seem relatively easy to support, unlike <a href="https://en.wikipedia.org/wiki/Dependent_type" rel="noreferrer">dependent types</a> which are more powerful and can represent types like <code>0..n</code> where <code>n</code> isn't a compile-time constant. So what are the reasons why most statically-typed languages don't support integer range types?</p>

<types><static-typing><integers>
Why don't many languages have integer range types?
CC BY-SA 4.0
2023-06-30T15:31:29.563Z
I'm sure this is domain-dependent, but I rarely encounter integers bounded to compile-time-constant ranges. And when I do, it's not something I'd encourage, like fixed-length buffers and "the user count fits in a ushort" assumptions.
BoppreH
CC BY-SA 4.0
2023-06-30T15:36:57.960Z
@BoppreH All languages with fixed-width integers require programs to make such assumptions, just with quite wide ranges like `0..4294967295`, but they don't provide the advantages of letting the user specify that range arbitrarily, and they don't prevent overflows or arithmetic errors. That said, the main use-cases are for integers which are logically bounded (like an index into a constant array), not integer values which theoretically ought to be unbounded but must be bounded for practical reasons (like a counter for the number of users).
kaya3
CC BY-SA 4.0
2023-06-30T15:39:22.733Z
That said, the answer might well be "because not enough people need integer range types" and that wouldn't be too surprising, but I'd like to know if there are other reasons, or e.g. if there's some empirical evidence showing that few programs would benefit from them. The kind of use-cases I'm thinking of are like `grid[x + 9 * y]` where `grid` is an array of length 81 in a sudoku program, where bounds-checking should be unnecessary because `x` and `y` are of type `0..8`.
kaya3
CC BY-SA 4.0
2023-06-30T15:45:48.333Z
How consistent are semantics for range types in different languages? You mentioned the case of Ada where `(T1: a..b) + (T2: c..d) == (T3: (a+c)..(b+d))`, but does every user of a range type want that behaviour instead of bound-checking that it stays within T1 or T2? In a template language like C++ it's fairly easy for the user to implement those kinds of types and you don't risk imposing unwanted semantics.
kouta-kun
CC BY-SA 4.0
2023-06-30T15:49:34.637Z
@kouta-kun If the user wants arithmetic to stay within `a..b`, but the arithmetic expression they write doesn't logically have a result that is necessarily in that range, then they need to do a bounds check before they can assign that value back to a variable of type `a..b`. So the semantics of `(a+c)..(b+d)` also allow the alternative semantics in a straightforward way, and both ways require the user to handle the condition when the arithmetic result is out of bounds. That said, if there are with languages with more restrictive range types like you describe, answers could mention them.
kaya3
CC BY-SA 4.0
2023-06-30T15:54:16.183Z
Right but you mentioned for example fixed width integers, for which behaviour depends on the language. C and its' descendants for example usually consider that `short + short` upgrades to `int` just in case the result does overflow out of a short, while Rust has an entire family of functions for different overflow handling. In short I, as an user, wouldn't really trust a language's range type behaviour to be the one I expect unless I implement it myself in a template or similar.
kouta-kun
CC BY-SA 4.0
2023-06-30T15:56:08.550Z
@kouta-kun Well, the behaviour of integer range types is fairly easy to document, and I think users ought to trust the documentation. If they don't then there is a much deeper problem.
kaya3
CC BY-SA 4.0
2023-06-30T16:44:44.133Z
Integer range types are actually pretty common in newer languages. Rust and Kotlin are two more I can think of off the top of my head
Seggan
CC BY-SA 4.0
2023-06-30T17:10:14.367Z
@Seggan-OnStrike As far as I know, Rust does not have integer range types; it does have integer range *values*, i.e. there is a [Range type in the standard library](https://doc.rust-lang.org/std/ops/struct.Range.html) for representing ranges as values, and a specific syntax for constructing `Range` values. So in Rust `0..100` is a value but it is not a type.
kaya3
CC BY-SA 4.0
2023-07-01T04:32:53.580Z
This is one of those things that probably sounds attractive when you first think of it, but isn't as useful as it seems. Similar to defining types for units.
Barmar
CC BY-SA 4.0
2023-07-01T15:55:50.147Z
It took me a while to realize that you mean that `0..100` defines a *new type* that is a restriction of integers, rather than a value that represents an integer sequence
Karl Knechtel
CC BY-SA 4.0
2023-07-02T00:30:57.900Z
@KarlKnechtel It can be both! When dealing with Singleton, Refinement and Dependent Types, a type can be interpreted as synonymous with a set of value, at which point `x..y` is a Refinement Type `{ n ∈ ℤ | x ⩽ n < y }` which a programming language may represent no differently than "a value that represents an integer sequence."
Longinus
CC BY-SA 4.0
2023-07-02T21:09:21.333Z
Just for completeness : Ada's range boundaries aren't necessarily compile time constants either.
user_1818839
CC BY-SA 4.0
2023-07-24T13:03:01.057Z
As worded, this question looks like a survey, which would require for each considered language to get a word from their designers as to why the feature was left out: they may not have considered it, may not have cared for it, may have thought it useless, may have thought it subsumed by another feature, etc... Since the answers are NOT performing a survey so far, I'd suggest asking, instead, why a language _would not_ support this feature... this let answers be backed by lifting reasons from existing languages, without forcing them to.
Matthieu M.
CC BY-SA 4.0
2023-07-24T13:08:50.137Z
@MatthieuM. I don't see the problem, and the answerers so far don't seem to see one either. *"So what are the reasons why ..."* doesn't preclude the (very likely) possibility the the same reasons apply to many or most languages. Still, if you think it's possible to improve the wording by an edit, please make one.
kaya3
<p>It seems like ranges might solve problems like out-of-bounds checks.</p>
<pre><code>def f(array, index): return array[index]
</code></pre>
<p>I could add a type for <em>array</em> that specifies the size of the <em>array</em>, but the safety of this function depends on the <em>relationship</em> between <em>array</em>'s range of valid indices and <em>index</em>'s value.</p>
<p>Types are about a value, but <strong>contracts</strong> are about relationships between values, so I think a contract system that incorporates range arithmetic would be more useful than merely range types.</p>
<p>I believe <a href="https://github.com/google/wuffs" rel="nofollow noreferrer">Wuffs</a> uses ranges and contracts because it is a designed for writing codecs and codecs often have to deal with binary files with layout determined by the file format specification.</p>

CC BY-SA 4.0
2023-07-05T00:43:21.320Z
2023-07-05T00:43:21.320Z
Mike Samuel
CC BY-SA 4.0
2023-07-05T00:46:16.013Z
Welcome! In a language like C++ with templates that are expanded before type-checking, you would be able to write this function generically, with a parameter for the length of the array. In that case `N` would be a fixed constant when the template gets expanded, and then the range type `0..N` would be expressible.
kaya3
CC BY-SA 4.0
2023-07-05T00:52:37.847Z
Yeah.  Compile-time metaprogramming often lets you do that if you know statically the length of the array.
Contract systems let you push the responsibility to the caller: don't call me unless you checked the bounds or have a contract that pushes responsibility yet farther up.
Mike Samuel
<p>Generics or templates are relatively new. Many languages with generics don't have this concept when the language was firstly created. And it would be too late for them to include range types as a basic feature later.</p>
<p>Without generics, and mechanisms to read information from types, specifically the type parameters of a generic, there is little to no observable effects of a variable being in the range type instead of a full integer. Range checking could be a thing, but that tends to be optimized out even in languages with range types. And without generics, it may not be easy for a programmer to specify the exact behavior after failing a range check.</p>
<p>Notably in Pascal-like languages, including Ada, range types are used to specify array dimensions. I'd say it's a very reasonable design. But the problem is we use arrays with dynamic sizes much more in modern programming, and won't really care that much about the few cases that still need fixed size arrays. The benefits couldn't apply easily in arrays with dynamic sizes.</p>

CC BY-SA 4.0
2023-07-03T11:11:33.813Z
2023-07-03T11:11:33.813Z
user23013
<p>Among the true and potentially useful statements one could make describing aspects of a program's execution, the fraction that can be easily statically proven is often only a small portion of the fraction that can be statically proven at all.  That in turn is only a small fraction of the portion which would be true for all inputs a program could possibly receive, which is often in turn only a small fraction of the portion that would be true for all inputs that a program would be expected to process usefully.</p>
<p>Range types may be useful in some situations where one wants to be able to prove things that are, in fact, easy to prove statically.  For example, if a program reads in six four-digit decimal integers and adds them all together, a compiler that understands the process of reading four-digit decimal numbers could fairly easily statically prove that the result would fit in a sixteen-bit unsigned integer.</p>
<p>If a language had range types with the semantics that trapping program execution if a range was exceeded, or performing arithmetically-correct computations without regard for the range, would both be equally acceptable, then range types could improve efficiency by letting compilers judge the cost of doing a range check versus having to support computations with larger numbers, and select whichever course of action would be more efficient.  Unfortunately, compiler philosophy today seems more focused on &quot;anything can happen&quot; UB which doesn't satisfy any program requirements than on trying to generate the most efficient machine-code programs <em>that actually accomplish what needs to be done</em>.</p>

CC BY-SA 4.0
2023-07-24T19:58:28.360Z
2023-07-24T19:58:28.360Z
supercat
<p>The most popular reason is simply that having additional types is more work for the authors of compilers and libraries.  <a href="http://stronglyemergent.com/blog/2013/negative-100-points/" rel="noreferrer">Every feature starts with -100 points</a>, and only gets implemented when there's a compelling reason to do so.</p>
<p>In low-level languages like C, automatic bounds-checking was deliberately avoided because it was seen as <em>inefficient</em>.  Better to let a program crash with a segfault or divide-by-zero error than to waste precious CPU instructions validating conditions that the programmer should have already explicitly checked.</p>
<p>If you do implement range types, you'll have to figure out the semantics of doing <em>arithmetic</em> on them.  So, using your example of <code>a: 10..20</code> and <code>b: 5..7</code>, you'd need:</p>
<ul>
<li><code>a + b: 15..27</code></li>
<li><code>a - b: 3..15</code></li>
<li><code>a * b: 50..140</code></li>
<li><code>a / b: 1..4</code></li>
</ul>
<p>What happens if you define <code>c : 1..100</code> and then write <code>c = a * b;</code>?  Do you give a compile-time error?  Throw an exception at runtime if <code>a * b &gt; 100</code>?</p>
<p>Moreover, a lot of real-world values just don't have well-defined bounds.  For example, if you're writing a <code>Date</code> class based on the Gregorian calendar, defining <code>month: 1..12</code> and <code>day: 1..31</code> seems obvious, but what bounds do you put on <code>year</code>?  Do you start with <a href="https://en.wikipedia.org/wiki/Year_zero" rel="noreferrer">0</a>, 1, <a href="https://learn.microsoft.com/en-us/windows/win32/sysinfo/file-times" rel="noreferrer">1601</a>, <a href="https://stackoverflow.com/questions/3310569/what-is-the-significance-of-1-1-1753-in-sql-server">1753</a>, 1900, <a href="https://en.wikipedia.org/wiki/Unix_time" rel="noreferrer">1970</a>, 2000, or <a href="https://en.wikipedia.org/wiki/Hebrew_calendar" rel="noreferrer">-3760</a>?  Do you end with <a href="https://en.wikipedia.org/wiki/Year_2038_problem" rel="noreferrer">2038</a>, 2099, 2999, or 9999?  You'd have to make an arbitrary decision.</p>
<p>It's just <em>easier</em> to use plain <code>int</code>.</p>

CC BY-SA 4.0
2023-06-30T16:47:01.370Z
2023-06-30T16:47:01.370Z
dan04
CC BY-SA 4.0
2023-06-30T17:15:00.963Z
*"Do you give a compile-time error?"* ─ Yes, specifically a type error because the type of the expression `a * b` does not match the type of the variable you want to assign it to. If you want the value of `a * b` without having to check that the result is in range, you can just let the compiler infer what range the result is in, or you can declare `c` as an unbounded integer. The last paragraph of your answer seems to assume that range types and plain `int` cannot coexist, but support for range types doesn't mean you can't also have an `int` type.
kaya3
CC BY-SA 4.0
2023-06-30T17:15:44.047Z
What happens with `c = a * b;`?  Of course it's a compile-time error. The meaning of `a: 10..20` is that `a` can take the value 20, and so forth, so you'll have overflow for some values, and the compiler is telling you so.
Pablo H
CC BY-SA 4.0
2023-06-30T17:28:50.830Z
Also, plain `int` _is_ an inter ranged type, as OP said. The compiler just doesn't flag potential overflows (except in some case where it has static/literal values). Just check out how many commits FFmpeg has for code with overflow errors detected by fuzzer...
Pablo H
CC BY-SA 4.0
2023-06-30T17:42:57.003Z
Plain `int` isn't an integer range type and I haven't said that it is; what I did say is that plain `int` types still require the programmer to make assumptions about what range their values will be in, due to overflow, unless the plain `int` type in your language is a bigint. The thing that makes `int32` semantically different to `-2147483648..2147483647` is that `int32` arithmetic will overflow to stay in bounds, whereas arithmetic on range-typed integers will produce results of a different range type.
kaya3
CC BY-SA 4.0
2023-08-15T15:09:15.787Z
I take issue with this answer.  Ada is a lower-level language than C: it doesn't abstract away bitwise struct layout details, which is very important for microcontrollers.  Yet, Ada has these bounded integer range types!
hegel5000
CC BY-SA 4.0
2023-08-15T15:19:59.783Z
Also, keep in mind that segfaults and processes that can be crashed from and the like are fancy-schmancy OS facilities.  When you're doing _really_ low-level programming, you don't have those!
hegel5000
<p><strong>TL;DR: Integer Ranges are a poor, niche feature, subsumed by (Constant) Generics.</strong></p>
<h3>Primitive Obsession</h3>
<p>Many developers, especially junior developers, tend to <em>underuse</em> static type systems, instead using <code>int</code> or <code>string</code> as the type for every single &quot;scalar&quot;. At the extreme, this is known as Primitive Obsession: only deviating from primitive types when forced to.</p>
<p>Underusing the type system means that the compiler cannot assist you. For example, if a function takes 3 parameters: Year (Int), Month (Int), and Day (Int) and the caller passes Day/Month/Year... there will be no compile-time error pointing out their mistakes.</p>
<p>Unfortunately, Integer Ranges lead the user astray towards <em>more</em> Primitive Obsession.</p>
<p>The very example of using <code>1..12</code> for a Month, while well-meaning, demonstrates this very issue: <code>1..12</code> is also the exact range for the Hour hand of a clock... even though both have different semantics from one another.</p>
<h3>Narrow Scope</h3>
<p>There are many invariants which may make sense, and the bounds of a range are only one such invariant. It may make sense to restrict an integral to be either a power of 2, a power of 10, a prime, an odd or even number, etc...</p>
<p>Integer Ranges bless the range as a special invariant, and completely (and dubiously) ignore all others.</p>
<h3>(Constant) Generics</h3>
<p>Constant generics -- the ability to parameterize by constants -- allow to implement Integer Ranges as library, as well as an infinite number of invariants: the user imagination is the limit.</p>
<p>Hence, in any language with support for Constant Generic, Integer Ranges are syntactic sugar for what could be a library feature...</p>
<p>Examples:</p>
<ul>
<li>C++: <a href="http://doublewise.net/c++/bounded/" rel="nofollow noreferrer">bounded_integer</a> library, by David Stone.</li>
</ul>
<h3>Additional Integration Work</h3>
<p><em>Still, why not?</em></p>
<p>One of the difficulty with any language feature is that it rarely is completely orthogonal from all other features.</p>
<p>In particular, meta-programming -- be it via macros, templates, generics, mix-ins, ... -- is generally all-encompassing, interacting with every single other feature.</p>
<p>As an example, Checked Exceptions and Template/Generics are a notorious example of poor integration, both in Java and C++, as there are no good facilities to manipulate the checked exception list. This results in <a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html" rel="nofollow noreferrer"><code>Stream.map</code></a> having no exception specification, when it should transparently propagate them.</p>
<p>This means that adding Integer Ranges in a language which supports, or plans to support, meta-programming requires <em>extra work</em> to properly integrate the two features.</p>

CC BY-SA 4.0
2023-07-24T13:28:11.877Z
2023-07-25T09:05:06.550Z
2023-07-25T09:05:06.550Z
Matthieu M.
CC BY-SA 4.0
2023-07-24T13:34:39.523Z
Could you show an example of how you would use constant generics (e.g. in Rust) to implement range types where `a + b` would have type `15..27` as in the question?
kaya3
CC BY-SA 4.0
2023-07-24T14:09:42.213Z
@kaya3-supportthestrike: Can I use C++ instead? Rust Const Generics story is still a wee bit underwhelming ;)
Matthieu M.
<p>In addition to the other fine answers, I'll note that many languages outsource their underlying type system to a &quot;lowest common denominator&quot; type system provided by a runtime, and then layer some small additional features on top of that.  The languages of the .NET runtime all have basically the same type system with minor variations; similarly for the JVM languages, and so on.</p>
<p>This has pros and cons for the language designer. On the pro side, if you like the type system then you save on time and effort designing one. On the con side, we spent a fair amount of time on the C# team asking &quot;why does the verifier not like this typesafe-in-C# program?&quot; and discovering that there was some subtle rule of the runtime's type verifier that we were violating.</p>
<p>But the big benefit for a platforms company like Microsoft is interoperability between languages: that you can write libraries in one language and use them in any compatible language.</p>
<p>That then leads to the big cost: what happens when two or more languages disagree on the design for a type not represented in the underlying type system, or represented in a way that doesn't meet all the needs of a particular language? We saw this play out between C# and F# over some decades as the designs for various kinds of tuple types evolved.</p>
<p>If the designers of C# wanted to add ranged integers -- and it's an entirely reasonable suggestion on the face of it -- one of the first pushbacks they'd give you is &quot;<em>will adding this feature to C# create work for the runtime team, the F# team, or anyone else who provides a language in this ecosystem?</em>&quot;</p>
<p>Creating work for others is a lot of points against a feature, particularly when there are potential new features that produce <em>benefits</em> for maintainers of other languages in the ecosystem, rather than <em>costs</em>.</p>

CC BY-SA 4.0
2023-07-24T16:15:07.007Z
2023-07-24T16:15:07.007Z
Eric Lippert
# Why don't many languages have integer range types?
Posted by: username_0 on 2023-06-30
An integer range type like `0..100` means that a value can be any integer in that range. Arithmetic operations on range types produce other range types, for example if `a: 10..20` and `b: 5..7` then `a + b: 15..27`. (Each range here includes both endpoints, for the sake of the example.) To be clear, here `0..100` is a type, not a value; the values are integers in that range.

Checking if a number is in a range can be treated as a fallible type conversion, e.g. converting `x` to type `0..100` could return an optional value which is `None` if `x` is not in that range. Alternatively, it can be treated as a type predicate, so that `if(x in 0..100) { ... }` narrows the type of `x` within the conditional branch.

Range types are used in a few languages, notably [Ada](https://en.wikipedia.org/wiki/Ada_(programming_language)#Data_types) (which *only* has integer types with defined ranges). They have some benefits, such as eliding bounds-checking for arrays of known length, avoiding overflows by using wide enough integers for any operation, and preventing division-by-zero errors. They also seem relatively easy to support, unlike [dependent types](https://en.wikipedia.org/wiki/Dependent_type) which are more powerful and can represent types like `0..n` where `n` isn't a compile-time constant. So what are the reasons why most statically-typed languages don't support integer range types?
**Tags:** types static-typing integers
> **Comments:**
> **username_1**: I'm sure this is domain-dependent, but I rarely encounter integers bounded to compile-time-constant ranges. And when I do, it's not something I'd encourage, like fixed-length buffers and "the user count fits in a ushort" assumptions.  
> **username_0**: @BoppreH All languages with fixed-width integers require programs to make such assumptions, just with quite wide ranges like `0..4294967295`, but they don't provide the advantages of letting the user specify that range arbitrarily, and they don't prevent overflows or arithmetic errors. That said, the main use-cases are for integers which are logically bounded (like an index into a constant array), not integer values which theoretically ought to be unbounded but must be bounded for practical reasons (like a counter for the number of users).  
> **username_0**: That said, the answer might well be "because not enough people need integer range types" and that wouldn't be too surprising, but I'd like to know if there are other reasons, or e.g. if there's some empirical evidence showing that few programs would benefit from them. The kind of use-cases I'm thinking of are like `grid[x + 9 * y]` where `grid` is an array of length 81 in a sudoku program, where bounds-checking should be unnecessary because `x` and `y` are of type `0..8`.  
> **username_2**: How consistent are semantics for range types in different languages? You mentioned the case of Ada where `(T1: a..b) + (T2: c..d) == (T3: (a+c)..(b+d))`, but does every user of a range type want that behaviour instead of bound-checking that it stays within T1 or T2? In a template language like C++ it's fairly easy for the user to implement those kinds of types and you don't risk imposing unwanted semantics.  
> **username_0**: @kouta-kun If the user wants arithmetic to stay within `a..b`, but the arithmetic expression they write doesn't logically have a result that is necessarily in that range, then they need to do a bounds check before they can assign that value back to a variable of type `a..b`. So the semantics of `(a+c)..(b+d)` also allow the alternative semantics in a straightforward way, and both ways require the user to handle the condition when the arithmetic result is out of bounds. That said, if there are with languages with more restrictive range types like you describe, answers could mention them.  
> **username_2**: Right but you mentioned for example fixed width integers, for which behaviour depends on the language. C and its' descendants for example usually consider that `short + short` upgrades to `int` just in case the result does overflow out of a short, while Rust has an entire family of functions for different overflow handling. In short I, as an user, wouldn't really trust a language's range type behaviour to be the one I expect unless I implement it myself in a template or similar.  
> **username_0**: @kouta-kun Well, the behaviour of integer range types is fairly easy to document, and I think users ought to trust the documentation. If they don't then there is a much deeper problem.  
> **username_3**: Integer range types are actually pretty common in newer languages. Rust and Kotlin are two more I can think of off the top of my head  
> **username_0**: @Seggan-OnStrike As far as I know, Rust does not have integer range types; it does have integer range *values*, i.e. there is a [Range type in the standard library](https://doc.rust-lang.org/std/ops/struct.Range.html) for representing ranges as values, and a specific syntax for constructing `Range` values. So in Rust `0..100` is a value but it is not a type.  
> **username_4**: This is one of those things that probably sounds attractive when you first think of it, but isn't as useful as it seems. Similar to defining types for units.  
> **username_5**: It took me a while to realize that you mean that `0..100` defines a *new type* that is a restriction of integers, rather than a value that represents an integer sequence  
> **username_6**: @KarlKnechtel It can be both! When dealing with Singleton, Refinement and Dependent Types, a type can be interpreted as synonymous with a set of value, at which point `x..y` is a Refinement Type `{ n ∈ ℤ | x ⩽ n < y }` which a programming language may represent no differently than "a value that represents an integer sequence."  
> **username_7**: Just for completeness : Ada's range boundaries aren't necessarily compile time constants either.  
> **username_8**: As worded, this question looks like a survey, which would require for each considered language to get a word from their designers as to why the feature was left out: they may not have considered it, may not have cared for it, may have thought it useless, may have thought it subsumed by another feature, etc... Since the answers are NOT performing a survey so far, I'd suggest asking, instead, why a language _would not_ support this feature... this let answers be backed by lifting reasons from existing languages, without forcing them to.  
> **username_0**: @MatthieuM. I don't see the problem, and the answerers so far don't seem to see one either. *"So what are the reasons why ..."* doesn't preclude the (very likely) possibility the the same reasons apply to many or most languages. Still, if you think it's possible to improve the wording by an edit, please make one.  
---
## 6 answers
### Answer by username_12 on 2023-06-30. Score: 13
The most popular reason is simply that having additional types is more work for the authors of compilers and libraries. [Every feature starts with -100 points](http://stronglyemergent.com/blog/2013/negative-100-points/), and only gets implemented when there's a compelling reason to do so.

In low-level languages like C, automatic bounds-checking was deliberately avoided because it was seen as *inefficient*. Better to let a program crash with a segfault or divide-by-zero error than to waste precious CPU instructions validating conditions that the programmer should have already explicitly checked.

If you do implement range types, you'll have to figure out the semantics of doing *arithmetic* on them. So, using your example of `a: 10..20` and `b: 5..7`, you'd need:

* `a + b: 15..27`
* `a - b: 3..15`
* `a * b: 50..140`
* `a / b: 1..4`

What happens if you define `c : 1..100` and then write `c = a * b;`? Do you give a compile-time error? Throw an exception at runtime if `a * b > 100`?

Moreover, a lot of real-world values just don't have well-defined bounds. For example, if you're writing a `Date` class based on the Gregorian calendar, defining `month: 1..12` and `day: 1..31` seems obvious, but what bounds do you put on `year`? Do you start with [0](https://en.wikipedia.org/wiki/Year_zero), 1, [1601](https://learn.microsoft.com/en-us/windows/win32/sysinfo/file-times), [1753](https://stackoverflow.com/questions/3310569/what-is-the-significance-of-1-1-1753-in-sql-server), 1900, [1970](https://en.wikipedia.org/wiki/Unix_time), 2000, or [-3760](https://en.wikipedia.org/wiki/Hebrew_calendar)? Do you end with [2038](https://en.wikipedia.org/wiki/Year_2038_problem), 2099, 2999, or 9999? You'd have to make an arbitrary decision.

It's just *easier* to use plain `int`.
> **Comments:**
> **username_0** (2023-06-30): *"Do you give a compile-time error?"* ─ Yes, specifically a type error because the type of the expression `a * b` does not match the type of the variable you want to assign it to. If you want the value of `a * b` without having to check that the result is in range, you can just let the compiler infer what range the result is in, or you can declare `c` as an unbounded integer. The last paragraph of your answer seems to assume that range types and plain `int` cannot coexist, but support for range types doesn't mean you can't also have an `int` type.  
> **username_13** (2023-06-30): What happens with `c = a * b;`?  Of course it's a compile-time error. The meaning of `a: 10..20` is that `a` can take the value 20, and so forth, so you'll have overflow for some values, and the compiler is telling you so.  
> **username_13** (2023-06-30): Also, plain `int` _is_ an inter ranged type, as OP said. The compiler just doesn't flag potential overflows (except in some case where it has static/literal values). Just check out how many commits FFmpeg has for code with overflow errors detected by fuzzer...  
> **username_0** (2023-06-30): Plain `int` isn't an integer range type and I haven't said that it is; what I did say is that plain `int` types still require the programmer to make assumptions about what range their values will be in, due to overflow, unless the plain `int` type in your language is a bigint. The thing that makes `int32` semantically different to `-2147483648..2147483647` is that `int32` arithmetic will overflow to stay in bounds, whereas arithmetic on range-typed integers will produce results of a different range type.  
> **username_14** (2023-08-15): I take issue with this answer.  Ada is a lower-level language than C: it doesn't abstract away bitwise struct layout details, which is very important for microcontrollers.  Yet, Ada has these bounded integer range types!  
> **username_14** (2023-08-15): Also, keep in mind that segfaults and processes that can be crashed from and the like are fancy-schmancy OS facilities.  When you're doing _really_ low-level programming, you don't have those!  
---
### Answer by username_15 on 2023-07-24. Score: 8
In addition to the other fine answers, I'll note that many languages outsource their underlying type system to a "lowest common denominator" type system provided by a runtime, and then layer some small additional features on top of that. The languages of the .NET runtime all have basically the same type system with minor variations; similarly for the JVM languages, and so on.

This has pros and cons for the language designer. On the pro side, if you like the type system then you save on time and effort designing one. On the con side, we spent a fair amount of time on the C# team asking "why does the verifier not like this typesafe-in-C# program?" and discovering that there was some subtle rule of the runtime's type verifier that we were violating.

But the big benefit for a platforms company like Microsoft is interoperability between languages: that you can write libraries in one language and use them in any compatible language.

That then leads to the big cost: what happens when two or more languages disagree on the design for a type not represented in the underlying type system, or represented in a way that doesn't meet all the needs of a particular language? We saw this play out between C# and F# over some decades as the designs for various kinds of tuple types evolved.

If the designers of C# wanted to add ranged integers -- and it's an entirely reasonable suggestion on the face of it -- one of the first pushbacks they'd give you is "*will adding this feature to C# create work for the runtime team, the F# team, or anyone else who provides a language in this ecosystem?*"

Creating work for others is a lot of points against a feature, particularly when there are potential new features that produce *benefits* for maintainers of other languages in the ecosystem, rather than *costs*.
---
### Answer by username_8 on 2023-07-24. Score: 4
**TL;DR: Integer Ranges are a poor, niche feature, subsumed by (Constant) Generics.**

### Primitive Obsession

Many developers, especially junior developers, tend to *underuse* static type systems, instead using `int` or `string` as the type for every single "scalar". At the extreme, this is known as Primitive Obsession: only deviating from primitive types when forced to.

Underusing the type system means that the compiler cannot assist you. For example, if a function takes 3 parameters: Year (Int), Month (Int), and Day (Int) and the caller passes Day/Month/Year... there will be no compile-time error pointing out their mistakes.

Unfortunately, Integer Ranges lead the user astray towards *more* Primitive Obsession.

The very example of using `1..12` for a Month, while well-meaning, demonstrates this very issue: `1..12` is also the exact range for the Hour hand of a clock... even though both have different semantics from one another.

### Narrow Scope

There are many invariants which may make sense, and the bounds of a range are only one such invariant. It may make sense to restrict an integral to be either a power of 2, a power of 10, a prime, an odd or even number, etc...

Integer Ranges bless the range as a special invariant, and completely (and dubiously) ignore all others.

### (Constant) Generics

Constant generics -- the ability to parameterize by constants -- allow to implement Integer Ranges as library, as well as an infinite number of invariants: the user imagination is the limit.

Hence, in any language with support for Constant Generic, Integer Ranges are syntactic sugar for what could be a library feature...

Examples:

* C++: [bounded\_integer](http://doublewise.net/c++/bounded/) library, by David Stone.

### Additional Integration Work

*Still, why not?*

One of the difficulty with any language feature is that it rarely is completely orthogonal from all other features.

In particular, meta-programming -- be it via macros, templates, generics, mix-ins, ... -- is generally all-encompassing, interacting with every single other feature.

As an example, Checked Exceptions and Template/Generics are a notorious example of poor integration, both in Java and C++, as there are no good facilities to manipulate the checked exception list. This results in [`Stream.map`](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html) having no exception specification, when it should transparently propagate them.

This means that adding Integer Ranges in a language which supports, or plans to support, meta-programming requires *extra work* to properly integrate the two features.
> **Comments:**
> **username_0** (2023-07-24): Could you show an example of how you would use constant generics (e.g. in Rust) to implement range types where `a + b` would have type `15..27` as in the question?  
> **username_8** (2023-07-24): @kaya3-supportthestrike: Can I use C++ instead? Rust Const Generics story is still a wee bit underwhelming ;)  
---
### Answer by username_9 on 2023-07-05. Score: 2
It seems like ranges might solve problems like out-of-bounds checks.
def f(array, index): return array[index]

I could add a type for array that specifies the size of the array, but the safety of this function depends on the relationship between array's range of valid indices and index's value.
Types are about a value, but contracts are about relationships between values, so I think a contract system that incorporates range arithmetic would be more useful than merely range types.
I believe Wuffs uses ranges and contracts because it is a designed for writing codecs and codecs often have to deal with binary files with layout determined by the file format specification.
> **Comments:**
> **username_0** (2023-07-05): Welcome! In a language like C++ with templates that are expanded before type-checking, you would be able to write this function generically, with a parameter for the length of the array. In that case `N` would be a fixed constant when the template gets expanded, and then the range type `0..N` would be expressible.  
> **username_9** (2023-07-05): Yeah.  Compile-time metaprogramming often lets you do that if you know statically the length of the array.
Contract systems let you push the responsibility to the caller: don't call me unless you checked the bounds or have a contract that pushes responsibility yet farther up.  
---
### Answer by username_10 on 2023-07-03. Score: 2
Generics or templates are relatively new. Many languages with generics don't have this concept when the language was firstly created. And it would be too late for them to include range types as a basic feature later.

Without generics, and mechanisms to read information from types, specifically the type parameters of a generic, there is little to no observable effects of a variable being in the range type instead of a full integer. Range checking could be a thing, but that tends to be optimized out even in languages with range types. And without generics, it may not be easy for a programmer to specify the exact behavior after failing a range check.

Notably in Pascal-like languages, including Ada, range types are used to specify array dimensions. I'd say it's a very reasonable design. But the problem is we use arrays with dynamic sizes much more in modern programming, and won't really care that much about the few cases that still need fixed size arrays. The benefits couldn't apply easily in arrays with dynamic sizes.
---
### Answer by username_11 on 2023-07-24. Score: 1
Among the true and potentially useful statements one could make describing aspects of a program's execution, the fraction that can be easily statically proven is often only a small portion of the fraction that can be statically proven at all. That in turn is only a small fraction of the portion which would be true for all inputs a program could possibly receive, which is often in turn only a small fraction of the portion that would be true for all inputs that a program would be expected to process usefully.

Range types may be useful in some situations where one wants to be able to prove things that are, in fact, easy to prove statically. For example, if a program reads in six four-digit decimal integers and adds them all together, a compiler that understands the process of reading four-digit decimal numbers could fairly easily statically prove that the result would fit in a sixteen-bit unsigned integer.

If a language had range types with the semantics that trapping program execution if a range was exceeded, or performing arithmetically-correct computations without regard for the range, would both be equally acceptable, then range types could improve efficiency by letting compilers judge the cost of doing a range check versus having to support computations with larger numbers, and select whichever course of action would be more efficient. Unfortunately, compiler philosophy today seems more focused on "anything can happen" UB which doesn't satisfy any program requirements than on trying to generate the most efficient machine-code programs *that actually accomplish what needs to be done*.
---
