struct Point { int X; int Y; ... }
struct Size { int Width; int Height; ... }
In either case the data structure is (int, int). But they're typed differently: one is typed as Point and the other as Size. This makes it very annoying to write functions that are compatible with both. Let's say we write AddThenSquare(Point A, Point B) {
return new Point((A.X + B.X)*(A.X + B*X),
(A.Y + B.Y)*(A.Y + B.Y));
}
To make Size compatible with that function you need to either refactor your code so that both Point and Size adhere to the same interface, or alternatively implement conversion functions. The latter option is what C#'s .NET framework opts for where Point and Size are taken as parameters to each others constructors. But it doesn't scale. What happens when you use a third party math library with its own point implementation? Your number of conversion functions grow by n*(n-1) where n is the number of classes that need to be converted between. To be fair I think C# has technical reasons for not having an interface between Point and Size (structs don't play nicely with interfaces) but even if it was included it would still be a royal pain making sure all other math libraries adhered to the same one.In practice it's rare anyone will have the foresight to put interfaces everywhere they're needed, and it's even rarer that programmers will use the same interfaces across libraries. It begins to sound like the "specialization within functions that inhibits and penalizes casual cooperation" that Alan Perlis was talking about.
At some point you start to wonder why the compiler cares at all what the name of the data is if the underlying information is the same. You could have dodged the headache altogether if you were in Python where a point is a tuple (x, y).
Still I'm not convinced static typing is to blame... it's more that the kind of static typing employed by Java, C++, C# is especially rigid.