Machine language (and Assembly language) don't have the concept of data types, so if you want to add an int and a float variable in Assembly, you have to use the appropriate Assembly instruction that adds an int and a float.
Erm... this sounds as if you're mixing up the idea of data types and operations on these. Data types are memory structures. Operations are an independent unit. And just because some languages do provide operators that can be used with multiple data types, doesn't mean they do in general and always. For example in C the sine function is defined as:
double sin(double x)
This means feeding anything but a double, for example an integer, will screw it up. Much like using a floating point operation (like FSIN) on an x87 will choke if an integer is handed as parameter.
Long story short, Assembler does have data types and does obey them (*1). For example on a /360 (1964) that would be:
Type Example Alignment
Character C'1234' Byte
Binary B'0101' Byte
Packed (BCD) P'1234' Byte
Decimal Z'1234' Byte
Char (hex) X'1234' Byte
Integer 16 Bit H'1234' Halfword
Integer 32 Bit F'1234' Word
Float (32 bit) E'-12.34' Word
Float (64 bit) D'-12.34' Doubleword
Float (128 bit) L'-12.34' Doubleword
Pointer (32 Bit) A(1234) Word
Pointer (16 Bit) Y(1234) Halfword
(There are also Q, S and V pointers, but that's extreme high level stuff :))
Using the wrong data type in an instruction will make the assembler throw a warning, exactly the same way as a C compiler does.
But if you are working with a high level language (for example: C), all you have to do is "mark" one variable with the int keyword and mark the other variable with the float keyword, and then use the '+' operator to add the two variables together, and the compiler will generate the machine language instruction that adds an int and float.
As said before, C does this only for a handful of predefined operators for convenience, not in general and all over. C99 resolved this in part by selecting one of several possible functions fitting the operand type(s), and C++ used overloading. Still, not by default and everywhere.
But I am wondering, which was the first programming language that had data types?
As shown, it's Assembly :))
Beside that, each and every programming language that was ever designed and implemented for a real machine does include data types. After all, without it won't operate, would it?
If the question is more about implied type conversion (and/or selection), then again Assembly will be a valid answer, as Assembly offers the same ways as C/C++ to write code that adapts to data types (*2). Now, if you insist to exclude Assembly for whatever ideological reason, then ALGOL 60 (*3) may be a good candidate. The sometimes cited FORTRAN introduced it quite late (*4) with FORTRAN 77 (in 1978) (*5) using intrinsics (introduced with FORTRAN 66).
*1 - Or better can, as many - let's say less proficient - programmers decide to ignore or even disable that feature.
*2 - As usual, the secret lies within meta programming - aka Macros - much you do overloading in C++. Except, Assembler does not even force you to use existing operators.
*3 - In fact, ALGOL is a very nice example for the issues of automatic conversion and how to handle it. Where ALGOL 60 added arbitrary type conversion, like its descendant C, ALGOL 68 restricted automatic type conversion later, to only work upward, to avoid program/data errors due to precision loss. So INT could be implied converted to FLOAT, but a downward conversion had to be explicit.
*4 - Which let people use explicit conversions way into the 80s, making it hard to update programs until today. A great example of the advantages of clear, stringent and centralized definition. The ability to switch from single to double or long with just a few changes, instead of debugging huge piles of old code to find each and every explicit conversion.
*5 - As another-dave pointed out in a comment IBM's Fortran II (of 1958) did automatic type conversion between float and int when assigning the result of an expression (See p.22 'Mode of an Arithmetic Statement' in the manual). The expression itself had to be, in all parts, either integer or float, thus it might not fit case made by the OP.
int i; float f;
to tagi
as capable of holding anint
andf
as holding afloat
. In many other languages, such as Lisp, Perl, Python, many Basics, JavaScript, and so on, the variables can hold any type, but the operators know what to do with the values in them because the values know their types.