Duck typing and safety.

simendsjo simen.endsjo at pandavre.com
Fri Aug 13 10:32:11 PDT 2010


On 13.08.2010 19:17, Steven Schveighoffer wrote:
> On Fri, 13 Aug 2010 13:01:47 -0400, simendsjo
> <simen.endsjo at pandavre.com> wrote:
>
>> While reading std.range, I though that a ducktyping design without
>> language/library support can be quite fragile.
>>
>> Consider the following example:
>>
>> import std.stdio;
>>
>> struct S
>> {
>> void shittyNameThatProbablyGetsRefactored() { };
>> }
>>
>> void process(T)(T s)
>> {
>> static if( __traits(hasMember, T,
>> "shittyNameThatProbablyGetsRefactored"))
>> {
>> writeln("normal processing");
>> }
>> else
>> {
>> writeln("Start nuclear war!");
>> }
>> }
>>
>>
>> void main()
>> {
>> S s;
>> process(s);
>> }
>>
>>
>> If you rename S's method, process() does something completely
>> different without a compile time error. By using interfaces this is
>> avoided as the rename would break the interface.
>>
>> Is there any idoms you can use to avoid stuff like this? Relying on
>> documentation doesn't seem like a good solution.
>
> You have somewhat missed the point of duck typing. It would look more
> like this:
>
> void process(T)(T s)
> {
> s.shittyNameThatProbabyGetsRefactored();
> }
>
>
> Basically, the point is, you compile *expecting* that you can call the
> function, and then when the type doesn't have the function, it simply
> fails.
>
> Of course, the error you get is not what you want, because to the
> compiler, it's not the call of the function that is the error, it's the
> compiling of the function that is the error.
>
> To remedy this, you use template constraints:
>
> void process(T)(T s) if(__traits(hasMember, T,
> "shittyNameThatProbabyGetsRefactored")
> {
> ...
> }
>
> And then the compiler won't even try to compile the function, it just
> fails at the call site.
>
> -Steve

Ok, point taken. But take a look at
void put(R, E)(ref R r, E e)
in std.range for instance. This function uses a member put if it exists, 
then front/popfront if it's an input range or opCall as a last instance.

It's easy to imagine such a design for other types where suddenly the 
program behaves differently because of a rename.

Is "put" a bad design?


More information about the Digitalmars-d-learn mailing list