Loop Abstractions in D revisited
In my previous post on Loop Abstractions in D I showed you how we could make loop constructs abstract, in a similar way which is common in Ruby. The example I used as a model was the retryable method from Cheah Chu Yeow. His version is customizable in a way that let you define the type of exception that triggers a retry.
retryable(:tries => 5, :on => OpenURI::HTTPError) do
# retryable code goes here
end
To mimic that in D we had to use templates, which are invoked with a special syntax.
retryable!(HTTPError)({
// Retryable code goes here
}, 5);
To be honest, I don’t like the template syntax. I don’t know why, it just doesn’t feel right. If possible, I’d much prefer a more native looking code. Maybe something like this:
retryable({
// Retryable code goes here
}, 5, HTTPError);
Christopher Wright points out an implementation that would be the closest one could get to a signature like that. He uses the somewhat primitive RTTI in D.
void retryable(ClassInfo info, int times,
void delegate() totry)
{
for (int i = 0; i < times; i++) {
try {
totry();
break;
} catch (Exception e) {
if (e.classinfo is info) continue; else throw e;
}
}
}
Which could be invoked with the following code.
retryable(HTTPError.classinfo, 5, {
// Retryable code goes here
});
The problem with this approach, which was pointed out by Jarret Billingsley, is that this implementation wouldn’t catch and retry on exceptions from derived classes (descendants to HTTPError in the above example). Fortunately, Jarret provides us with a solution.
What you have to do then is perform a dynamic cast. There’s no syntax for this, but you can
hack around with some of the runtime functions to do the same thing. Place:extern(C) Object _d_dynamic_cast(Object o, ClassInfo c);
somewhere in your module, then in the catch clause of retryable:
catch(Exception e) { if(_d_dynamic_cast(e, info)) continue; else throw e; }
That _should_ work. It’s doing the same thing as the cast operator would
but without the pretty syntax.
Not pretty, but it works, at least if you use one of the standard libraries: Tango or Phobos. I’m not sure it’s better than the template version though. The .classinfo property brings nearly as much noice as the template version does. Also, the template version has the advantage that it is resolved at compile-time.
I think I’ll go with templates after all. Who knows, I might even get used to them one day.
Cheers! 🙂
I don’t like templates…
Why not something simple like this?:
Cheers,
Javi
Interesting approach. Using a delegate to control the flow opens some intriguing applications.
I think you introduced a small bug though: totry will always be invoked times time. Easily fixed. Just replace
totry();
with
totry(); return;
Cheers man!
Ummm… it isn’t a bug, I wrote a function to call totry() N times. Rename retry() to CallNTimes() or something like that. ; )
My fault, I misunderstood the idea of the article.
Cheers!
Javi
How about:
retry!(HTTPError,5)({code});
?maybe you could use
mixin
to create nicer syntax?