Let's forget for the moment that processors have specific hardware for manipulating byte sequences of a particular size. Let's forget for the moment that processors have specific hardware for operating on specific interpretations of byte sequences (floating-point registers, SEE registers, etc).
What good does this abstraction do from a user perspective? After all, programming language exist for the convenience of the programmer. If the abstraction isn't making something convenient for the programmer, then the abstraction may as well not be there.
So the only fundamental type the language provides is a byte. And therefore every other type is some form of byte sequence. OK.
We don't want to have different programmers declaring different forms of the same types like float
or int32
or whatever. After all, one person's 4-byte float wouldn't be interoperable with someone else's 4-byte float. Not only that, different programmers would be wasting lots of time implementing the same thing.
So the standard library for such a language would have no choice but to include generally useful sequences and operations on them. There would be some equivalent of float
, double
, int
, etc.
So, what exactly have you gained compared to having these be language-supplied types? You still have to provide them, either way.
Programming languages (that aren't assembly, and even assembly to some degree) exist to provide useful abstractions to the user. They allow the user to program for an abstract model rather than the specific vagaries of a system, thus allowing the compiler to do the mechanical work of translating that abstract model to a real system.
However, an abstract model can sometimes get in the way of optimal transformations. Sometimes, it is because the abstract model defines too much. For example, Java defines pretty much everything about its basic types. float
is guaranteed to be a IEEE-754 32-bit floating point value. Which means that if you want to run a Java program on a platform that doesn't have IEEE-754 support in hardware, you must software-emulate all floating point math.
By contrast, C/C++ says nothing about what float
provides other than the fact that it can have values that aren't whole numbers. As such, a compiler for hardware that has no IEEE-754 support could convert float
operations into a form of fixed-point math. Or do something else unusual, but something faster than software emulation of IEEE-754 floats.
Your abstract model fails in the other direction. It specifies so little that it's actually under-specified compared to the hardware, thus making conversion into hardware operations harder. Hardware can act at the byte level, but actual CPUs operate at the register level: groups of bytes with some particular meaning. There are even different kinds of registers with different kinds of operations on them.
Consider what happens if you had your bit-array system that's based on 8-bit bytes, and then you take it to a system where the CPU uses 9-bit bytes (and yes, that's a real thing). C and C++ support such systems, precisely because they don't specify things at the bit level. An int
might be a 4-byte object, but it would be 36 bits in size. Your int32
would be useless on such a system, as the hardware can't effectively support it.
Finding the right abstraction for high-performance, one that's high enough to be a useful abstraction for the programmer, high enough to cover lots of hardware, yet low enough to not get in the way of the programmer, is a very difficult job.