| There is actually another option. A more sophisticated type system. Let's say you had some pseudocode like this: let a = 5
let b = 12
let c = a + b
The type of a would be Integer[5..5], the type of b would be Integer[12..12], the type of c would therefore be Integer[17..17]. In a more complex example: def foo(a: Integer[0..10], b: Integer[0..10]):
return a + b
The return type of this function would be Integer[0..20].This kind of type system can solve a number of issues, all but division by zero (which would probably still have to be solved with some kind of optional type). If type inference dictates that the upper range of an integer would be too large to physically store in a machine data type, then you either resort to bignums or you make it a compilation error. By adding modular and saturating integer types you can handle situations where you want special integer behaviours. By explicitly casting (with the operation returning an optional) you can handle situations where you want to bound the range. This drastically simplifies a lot of code by removing explicit bounds checks in all places except where they are absolutely necessary. If for some reason you care about the space or computational efficiency of the underlying machine type, you can have additional annotations (like C's u?int_(least|fast)[0-9]+_t). If you absolutely must map to a machine type (this is usually misguided, unless you are dealing with existing C interfaces, for which such a language can provide special types) you can have more annotations. Ada has something resembling this. I believe there are some other languages that implement similar features. I believe this sort of thing has a name, but I am not great with remembering the names of things. Hopefully this is some food for thought. |