First, to make sure we're talking about the same things, I would start with some definitions.
Static typing means that type errors are reported at compile time, while dynamic typing means that type errors are reported at runtime.
Duck typing means that a piece of code requires that an object supports the operations that are used and nothing more.
Structural typing requires that an object supports a given set of operations (even if some of them may not be used).
Nominal typing requires that object is exactly of the given type, or is a subtype of that type.
So, as you can see, structural typing is stricter than duck typing and nominal typing is stricter than structural.
Now, I'm going to talk about the TypeScript language, because it nicely illustrates most of these options.
Consider the following TypeScript program:
interface Person {
Name : string;
Age : number;
}
function greet(person : Person) {
alert("Hello, " + person.Name);
}
greet({ Name: "svick" });
Since the object that's passed to greet
doesn't have the Age
property, this causes a compile time error, demonstrating that TypeScript uses static structural typing.
Despite the error, the code above actually compiles to the following JavaScript, which runs fine:
function greet(person) {
alert("Hello, " + person.Name);
}
greet({ Name: "svick" });
This shows that TypeScript also uses dynamic duck typing.
If the code instead compiled to something like:
function greet(person) {
if (!(typeof(person.Name) == 'string' && typeof(person.Age) == 'number'))
throw 'TypeError';
alert("Hello, " + person.Name);
}
Then that would be an example of dynamic structural typing, because it checks that the object has the required properties of the required types, even if the function itself doesn't require them.
If it compiled to:
function greet(person) {
if (!(person instanceof Person))
throw 'TypeError'
alert("Hello, " + person.Name);
}
That would be an example of dynamic nominal typing, because it checks the name of the type of the object, not its structure.
What this all shows is that dynamic non-duck typing is possible (both structural and nominal). But this approach isn't used very often, because it mostly combines the disadvantages of non-duck typing (you have to specify types explicitly; less flexible) and dynamic typing (type errors only show at runtime and only in the code that actually runs).
If you're going to add type annotations to make non-duck typing possible, you might as well check the types at compile time.
1 + "1"
. In Python's case the discipline of checking is pretty much absent and it's up to implementation of user code to check the types if the user (opposed to Python's runtime) finds it useful. Also note that duck typing vs. non-duck typing is alike nominal vs. structural typing (see Wikipedia).