On Fri, 13 Aug 2010 13:01:47 -0400, simendsjo <[email protected]> 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

Reply via email to