Duck typing and safety.

Steven Schveighoffer schveiguy at yahoo.com
Fri Aug 13 10:17:25 PDT 2010


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


More information about the Digitalmars-d-learn mailing list