wallet/docs/duck_typing.md

98 lines
5.0 KiB
Markdown
Raw Normal View History

---
title: Duck Typing
---
Assume naming system based on Zookos 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 users machine.
The generality of run time duck typing is dangerous when you expect your
program to run on someone elses 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.
Pythons 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 dont.
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 users 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.