Home > D Programming Language > Loop Abstractions in D revisited

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! 🙂

Categories: D Programming Language Tags:
  1. Javi
    February 2nd, 2008 at 15:22 | #1

    I don’t like templates…
    Why not something simple like this?:

    void retry(uint times, void delegate() totry, void delegate(Exception) funcex=null) 
    { 
      for(uint i = 0; i < times; i++) 
      {
        try 
        {
          totry();
        } 
        catch(Exception ex) 
        { 
          if(funcex !is null) 
          { 
            funcex(ex); 
    
            // Or if you want to control the break the loop, 
            // define funcex as bool delegate(Exception) and: 
            // if(!funcex(ex)) 
            // { 
            //   break; 
            // }
          } 
        }
      } 
    } 
    
    // Some examples: 
    
    // Executes MyFunc() 5 times catching all exceptions 
    retry(5, &MyFunc); 
    
    // Executes MyFunc() 5 times catching all exceptions 
    // and pass them to HandleMyExceptions(Exception) 
    retry(5, &MyFunc, &HandleMyExceptions); 
    
    // More explicit examples: 
    retry(5, { writefln("hola"); });  
    
    retry(5, { writefln("hola"); throw(new Exception("error!")); }, delegate(Exception ex){ writefln(ex); });

    Cheers,
    Javi

    • February 3rd, 2008 at 19:26 | #2

      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!

      • Javi
        February 3rd, 2008 at 20:09 | #3

        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

  2. kl
    April 16th, 2008 at 20:27 | #4

    How about:
    retry!(HTTPError,5)({code}); ?

    maybe you could use mixin to create nicer syntax?

  1. No trackbacks yet.