So being able to define something as (in an arbitrary language) binary [8\8] would be nice, an 8 bit integer part and an 8 bit fraction part. [...] Is this already a thing, or is it too obscure to be worried about?
I have implemented something like that in Python, deriving from
tuple. Arithmetic, unary, and binary operators are just syntactic sugar on top of
special methods, so that e.g.
a*b is equivalent to
a.__mul__(b) (or if that is
NotImplemented, then
b.__rmul__(a)). (I called dividing the integral numerator and the denominator (
n/d) of a number, "normalization"; with divisor always positive, and only the numerator signed. You end up doing this basically after every operation, which makes arithmetic using such a the type surprisingly "slow". For Python, it doesn't matter much, because it is quite slow anyway.)
If I recall correctly, I used it for prime decomposition or something like that; a simple algorithmic solution to something, closer to HPC, and not really anything I'd need on a small microcontroller.
On the other hand, a very close cousin,
interval arithmetic can be very useful even on small microcontrollers (and with both integer and floating-point math). Essentially, each number is represented by an interval,
min..max. Operations like multiplication and division yield the largest interval when applied to the arguments.
(If you continue down that rabbit hole, you can represent each number by their probability distribution, for example their Gaussian probability distribution, so that the result of the same arithmetic operations you apply to scalar numbers, yields the probability distribution of the results when all inputs and constants are also specified as probability distributions. It can be quite useful when dealing with uncertain and fuzzy data. You will want to choose your basis distribution carefullt, though, and compose all other distributions as sums or variants of that. Gaussian itself is a good choice, but there are others as well.)
I am not in favour of generic operator overload or operator definition, in the sense of how e.g. C++ uses
>> and
<< with streams, but being able to define the functions corresponding to arithmetic, binary, and unary operators, is definitely useful with custom numeric types (fixed point formats, interval arithmetic, etc.) as well as vectors and matrices (and linear algebra in general). Exactly how this is done depends on your language and preferences, of course, but I do prefer most the model where the operator function is solely determined by the types of the arguments to the operator. For built-in arithmetic types (number types), the functions would be provided by the compiler. For custom types, you'd define one function per type pair (for arithmetic and binary operators) or one function per type (for unary operators). For example:
dfn operator_multiply(int a, vec2d b): vec2d(a*b.x, a*b.y); dfn operator_multiply(vec2d a, int b): vec2d(a.x*b, a.y*b); dfn operator_multiply(vec2d a, vec2d b): int(a.x*b.y - a.y*b.x); dfn operator_divide(vec2d a, int b): vec2d(a.x/b, a.y/b); dfn operator_divide(int a, vec2d b): forbidden;For optimization, it is quite important that the language has a concept of
"pure" functions: functions whose return value depends only on their arguments, and have no side effects, so that they can be evaluated at any point and always yield the same result – and that calls to such functions can be freely rearranged, cached, and even omitted. (The effect of such a concept for a compiler is easy to see, for example in Fortran 95 'forall' loops. Also, GCC and Clang implement this even for C via the
const function attribute,
__attribute__((const)).)
What I wonder myself, is whether developers would be confused with different "kinds" of functions –– like operators above, or say accessors that can only access their own object, but are otherwise "pure"/"const". If you look and ask around, it is clear that even the distinction between Fortran functions and subroutines (the former returning a value, the latter not returning anything) makes people ask questions. Would it be better to have separate function 'kinds', or just have attributes specifying their expected (and compile-time verified) properties?
I ended up in details like how to deal with say "pure" function when supplied with a parameter of a "volatile" type. You can solve it at the model level (ie. "operator functions will have their arguments passed by value" in which case the volatility of the value nullifies reuse but makes the same function "work"), or you can have a way to state parameter type requirements, somewhat akin to old K&R C function or Fortran 95 function or subroutine parameter definition.
The rabbit hole went so far for me that I looked into the possibility of having the object file format support the definition (down to which register a parameter is passed in, as well as its type) of each parameter. At some point it gets too far, as in requiring completely new file formats for the toolchain to work on, and not just a completely new toolchain...