98 lines
5.0 KiB
Markdown
98 lines
5.0 KiB
Markdown
|
---
|
|||
|
title: Duck Typing
|
|||
|
---
|
|||
|
Assume naming system based on Zooko’s triangle. At what point should
|
|||
|
human readable names with mutable and context dependent meanings be nailed
|
|||
|
down as globally unique identifiers?
|
|||
|
|
|||
|
The most flexible, most convenient, most powerful, most general, and thus
|
|||
|
most prone to disaster form of run time typing is duck typing, wherein the
|
|||
|
human readable name gets translated on the fly in the run time context,
|
|||
|
and the run time context is whatever is on the end users machine.
|
|||
|
|
|||
|
The optimal, most powerful, most powerful, most convenient typing that is
|
|||
|
safe is duck typing that defaults to *compile time* translation of
|
|||
|
human readable type identifiers into globally unique type identifiers,
|
|||
|
translating using the environment present on the end user’s machine.
|
|||
|
|
|||
|
The generality of run time duck typing is dangerous when you expect your
|
|||
|
program to run on someone else’s machine.
|
|||
|
|
|||
|
In fact it is dangerous even on your own machine. Python executing duck
|
|||
|
typed code produces surprising and strange results. C++ refuses to compile it
|
|||
|
without endless incomprehensible boilerplate. Haskel, on the other hand, for
|
|||
|
all its faults, just simply does as you thought you were telling it to do.
|
|||
|
|
|||
|
Python’s duck typing causes endless install grief. Once a complex program
|
|||
|
moves away from its home environment, endless install problems arise,
|
|||
|
because the objects returned are not exactly the objects expected.
|
|||
|
Successive versions of the API return objects that look less and less like
|
|||
|
the previous versions.
|
|||
|
|
|||
|
This, of course, is exactly the problem that COM and its successor NET
|
|||
|
quite successfully solved, but the solution relies on compilation. The
|
|||
|
compiled code running on a newer API is guaranteed to receive the sort of
|
|||
|
objects that the compile time API would have given it or fail cleanly, even
|
|||
|
though the api and the compiled code were compiled by different people on
|
|||
|
different and dissimilar machines.
|
|||
|
|
|||
|
Sometimes you want run time typing for flexibility, some times you don’t.
|
|||
|
|
|||
|
If your program is going to be used in foreign environment, you usually
|
|||
|
want types identified by human readable names, which can stand for many
|
|||
|
things, translated into types identified into globally unique identifiers
|
|||
|
by the translation environment on your machine, the machine on which you
|
|||
|
are debugging the code, rather than the end user’s machine. Duck typing is
|
|||
|
optimal when developing code that will only run on your own machine in
|
|||
|
your own environment.
|
|||
|
|
|||
|
Source code, therefore, should come with libraries mapping human readable names
|
|||
|
to the globally unique type names on which that source code was tested and
|
|||
|
depends.
|
|||
|
|
|||
|
The greatest flexibility is to have choice as to when local names will be
|
|||
|
bound to globally unique identifiers, compile time, or run time. To avoid
|
|||
|
install hell, should default to compile time, except where run time duck
|
|||
|
typing is explicitly invoked..
|
|||
|
|
|||
|
Forty nine times out of fifty the compiler can type objects better than
|
|||
|
you can, and ninety nine times out of a hundred the run time can type
|
|||
|
objects better than you can, and with vastly less effort, but it is that
|
|||
|
one time out of fifty, and the one time out of a hundred, that bites you.
|
|||
|
|
|||
|
Haskel has by far the best duck typing system, producing predictable,
|
|||
|
expected, and intended results, without rigidity or incomprhensible boiler
|
|||
|
plate, unlike C++ metacode. The programmer expresses his intention in a
|
|||
|
programmer intuitive way, and Haskel correctly divines his intent, translates
|
|||
|
into rigid compile time types as appropriate to the situation, or produces a
|
|||
|
relevant type error.
|
|||
|
|
|||
|
The C++11 `auto` type and `decltype` are sort of
|
|||
|
compile time duck typing, steps in that direction. `Decltype`
|
|||
|
is duck typing inside the elaborate C++ straitjacket. If `auto`
|
|||
|
references a `decltype`, that is pretty close to duck
|
|||
|
typing. Real duck typing is doubtless a lot better, but it is
|
|||
|
a good step.
|
|||
|
|
|||
|
However the C++11 `auto` and `decltype`
|
|||
|
require you to explicitly write polymorphism into your code using the
|
|||
|
convoluted complex template formalism and inheritance, whereas Python is
|
|||
|
casually polymorphic by default – and thus apt to produce wildly unexpected results.
|
|||
|
|
|||
|
But even when you are not in fact supporting polymorphism, `auto`,
|
|||
|
`decltype` and the C\# `var` find the correct types
|
|||
|
better than you do, avoiding unintended type conversions, which are a huge
|
|||
|
source of C++ bugs. So it is better to use auto and decltype
|
|||
|
wherever possible,whenever you do not want to explicitly force type
|
|||
|
conversion, wherever the exact type is not all that important to you.
|
|||
|
|
|||
|
Sometimes you really do care that this thing is a uint, it has got to be
|
|||
|
a uint, we are taking advantage of the fact that it is a uint, and if we
|
|||
|
turned it into a ulong or a short, or whatever, then the mechanism would
|
|||
|
break. But usually, particularly with the more complex types,
|
|||
|
the precise type is irrelevant noise and a useless distraction. You
|
|||
|
generally want to know what is being done, not how it is being done.
|
|||
|
Further, if you explicitly specify how it is being done, you are likely to
|
|||
|
get it wrong, resulting in mysterious and disastrous type conversions.
|
|||
|
|