13

This question has been asked here, but received poor answers and didn't clarify the issue. I believe it justifies asking it again.

I understand that you can have duck typing with either dynamically typed languages or with statically typed ones (but examples for these are rare, such as C++'s templates).

However I'm not sure if there is such a thing as a dynamically typed language without duck typing.

Duck typing means that the type of an object is based on the operations and attributes it has at a given point in time. Is there a way to do dynamic typing without inevitably supporting duck typing?

Let's look at this Python code for exampe:

def func(some_object)
    some_object.doSomething()

something = Toaster()
func(something)

In dynamically typed languages, the type of an object is only known at run time. So when you try to do an operation on it (e.g. some_object.doSomething()), the runtime has only one choice - which is to check whether or not the type of some_object supports doSomething(), which is exactly what duck typing is.

So is it possible to have a dynamically typed languages without duck typing? Please explain.

3
  • 1
    I've checked a few languages (Javascript, Ruby and PHP) on Wikipedia and the latter is supposed to be dynamic and weak, not duck typed. That may answer your question.
    – Trylks
    Oct 14, 2014 at 8:48
  • Actually the runtime has a choice to check the type signature of the object before trying to access any of its' fields or calling its' methods. That would be "strong dynamic typing". Note, that Python, for example, is partially working this way -- say, it explicitly issues a type error when you try 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). Oct 14, 2014 at 9:08
  • maybe you should use a goose instead of a duck to do your typing for you?
    – jwenting
    Oct 15, 2014 at 11:32

3 Answers 3

22

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.

9
  • 1
    I wouldn't say typescript uses dynamic duck typing. The fact that despite compile errors tsc still produces javascript sounds like an oddity to me, but anyway... the typing error was reported at compile time, and the runtime part is left to javascript, not typescript. However, typescript uses static duck typing also; when you write "var t = 1;", the variable is assumed to be a number.
    – Joel
    Oct 17, 2014 at 11:18
  • @Joel Yes, TypeScript inherits dynamic duck typing from JavaScript, but that doesn't make it any less a property of TypeScript. And your example demonstrates type inference not duck typing.
    – svick
    Oct 17, 2014 at 12:45
  • What would you call a language which required that methods be defined as parts of interfaces, but allowed storage locations to be typed using arbitrary combinations of interfaces? If passing [3,4] to some method of one collection holding [1,2] yields [1,2,3,4], and passing [3,4] to some a method of some collection holding [1,2] would [4,6], should the methods have the same name? Calling the first Sequence$Appendable$Add and the second NumericVector$Add but then being able to say that a variable should be interpreted as a Sequence or a NumericVector would...
    – supercat
    Oct 23, 2014 at 18:11
  • ...seem cleaner than simply hoping that homographic names won't cause confusion. In languages where each variable has to only be one type, a "purely dynamic" type might be helpful, but if composite types were available I would think that would eliminate 99% of usage cases for "out-of-the-blue" duck typing.
    – supercat
    Oct 23, 2014 at 18:13
  • I'm inclined to agree with @Joel the difference between a warning and exception has little to do with typing of the language. TypeScript is a library. tsc is an interface into it. If you use the library, it'll trigger an event. By default if nothing is listening you get a script. If you listen and throw an exception you can stop the script from being generated. Does that change the type-system of TypeScript? Of course not. Apr 18, 2018 at 15:30
3

Apparently (from what I read) Duck typing has meaning only in an object oriented context, when functions are attached as methods to objects. Then when you write duck.quacks(3), this will work if the current value of duck has the method quacks.

Dynamic typing is not necessarily attached to an OO view with methods.

You can define the type real with associated operator or function +: real*real->real, and the type rational with the associated operator +: rational*rational->rational. Then if you write a+b, in a dynamically checked time system, both your variables aand b may have a + operator, but you get a run-time type error.

Dynamic typing checks for categorial consistency of values, possibly several of them.

Duck typing checks for behavioral consistency of the code with an object at hand (a single one, as far as I understand it).

In a sense duck typing is a form of runtime polymorphism, except for the fact that it applies only to access methods of a single object.

However, one could possibly define a more general form of run-time polymorphism, where the operator + to be executed would be determined on the basis of all arguments. So that a duck and a chicken could danse together if they share a common danse function. They could have several so that ducks can danse also with geese with a different function. But that seems a bit complicated. As far as I remember, something of the kind (with probably more structure) may have been possible with the generic functions of the language EL1, a very old forerunner of object oriented languages.

2
  • Python supports operator overloading too. def add(a,b): return a+b is doable. It's converted to a.__add__(b) automatically. ... So yeah, duck typing seems to require some kind of advanced user defined types. Though data members also work, so duck structs would also be possible, similar to this: def addstuff(a,b,t): t.stuff = a.stuff + b.stuff
    – Weaver
    Oct 17, 2014 at 12:25
  • @StarWeaver Well yes. But what bothers me with your example is the assymetry between the operands. If there is a type error, it is either because a does not have the proper method, or because there is a type mismatch between the methods of a and the type of b. So, depending on the situation, you get a different exception (if my memory of Python is correct). I might want a level of abstraction where I would just get an exception saying that a and b cannot + together (sorry for the weird sentence). But that may be a matter of taste. I might also like to state that + is commutative.
    – babou
    Oct 17, 2014 at 12:39
-2

An answer by analogy:

Can you buy a convertible and never put the top down? Sure. It's probably not the best way to spend your resources, since you're paid extra for some features (e.g. convertible top, extra structural stiffening due to lack of roof as structural element) and gotten some worse results (e.g. extra road noise, possibly lower crash safety, smaller storage compartments) as a result of investing in that feature you won't use. But it's technically feasible.

It's the same with dynamic languages and duck typing. You've given up the higher efficiency and the compile-time type safety assurances of static languages. For what? Generally for the simplicity of duck typing. Variables and collections can hold anything, and you don't need to do a lot of up-front specifying just what. Mixed collections like [ 12, "gumbo", 12.4, 4+4j ] (an integer, a string, a floating point value, and a complex value) are trivial, and you don't have the constant type-casting you see in Java code (for example).

It's possible in a dynamic language like Python to create objects that are not duck-typed:

class Hungarian(object):
    self __init__(self):
        self.value = []
    self addInt(self, intValue):
        self.value.append(intValue)
    self addFloat(self, floatValue):
        self.value.append(floatValue)
    self addComplex(self, complexValue):
        self.value.append(complexValue)
    # ...

But as you might notice, there's no real checking of the types, and each of the methods is implemented with a duck-typed built-in structure (list). Having paid the price for dynamism, you might as well put the top down and get the resulting simplicity:

class NotHungarian(object):
    def __init__(self):
        self.value = []
    def add(self, whatever):
        self.value.append(whatever)
4
  • In what way is that Hungarian class not duck typed? As you pointed out, there is no checking of types.
    – svick
    Oct 14, 2014 at 17:00
  • @svick It's intended to be used in a type-specific way, like Java, with separate methods depending not on the action performed, but also on the types used. Duck typing would use the same method independent of the type being added, as NotHungarian does. Duck typing depends not just on not checking the type, but also using the same call/method/message name (add). (NotHungarian also uses a method name, add, that's common with other Python objects, such as set. It "quacks" like those other objects/classes.) Oct 14, 2014 at 17:47
  • 1
    This answer is a bit misinformed on the motivation to “give up the higher efficiency and the compile-time type safety assurances of static languages”. The motivation was to let go of the shackles of a Fortran- or C-like type system, because these systems often get in the way (think of Pascal's fixed-length strings), prevent generic programming, and make insanely powerful features like eval infeasible. The motivation never was to get rid of types, and some dynamic languages have “gradual typing”. MJD has a nice presentation on static vs. dynamic typing.
    – amon
    Oct 15, 2014 at 14:22
  • @amon Disagree. Statically typed languages generally have a pretty significant advantage over dynamically typed languages because they used unboxed/value types, structs instead of dicts, etc. JIT compilers have nicely narrowed the gap, but AOT static compiled languages still notably faster. That's not the only reason to choose a language. I use dynamic any time I possibly can, and the performance penalty is limited or unimportant most of the time. And yes, there are other motivations for choosing dynamic. But you do give up real things to go there. Oct 15, 2014 at 15:24

Not the answer you're looking for? Browse other questions tagged or ask your own question.