Type Propagation

In an expression, each token (constant or variable) has some value. The language rules specify how to apply each operator to the values, and what the result is. If we evaluate:

int x = 10, y = 21; double d = 3.7; 2.5*(x + y - 1) - d;
The steps will look like this:
x + yAdd 10 and 21 to make 31.
x + y - 1Subtract 1 from 31 to make 30.
2.5*(x + y - 1)Convert 30 to 30.0 and multiply to get 75.0.
2.5*(y + 1) - dSubtract 3.7 from 75.0 giving 71.3.

Just as the language has rules to operate on values, so with types:

x + yThe sum of integers is an integer.
x + y - 1The difference of integers is an integer.
2*(x + y - 1)The product of a float and an integer is a float.
2*(y + 1) - dThe difference of floats is a float.
These rules work up from the tokens to determine the type of any expression, just as evaluation rules determine its value.

While values must be computed at run time, in a statically-typed language, types can be determined at compile time. This simplifies computation of values at run time, since the types are already known. In a dynamically-typed language, the full translation of x + y might look something like:

if x and y are integers, add them with an integer addition;
else if x and y are float, add them with a float addition;
else if x and y are strings, call the string concatenation function;
else if one of x and y is a float and the other an integer,
    convert the integer to float and perform a float addition;
else raise a type error exception
If the language has more meanings for +, there will be more cases. But for a statically-typed language, the translation of x + y can just apply the correct operation, or produce a compile-time error if there is none. When the statement is in a loop, this can produce considerable efficiencies.

This is also a reason why statically-typed languages usually have homogeneous arrays. If array a is heterogeneous, then the type of a[i] can vary depending on the value of i, since different array positions can have different types. Because the value of i cannot (always) be known at compile time, the type of a[i] cannot be known either. But when a is homogeneous, the type of a[i] is constant.

A dynamically-typed language will have to check the type of a[i] at run-time anyway, so it has nothing to gain by making a homogeneous.