Le 21/02/2012 18:10, H. S. Teoh a écrit :
On Tue, Feb 21, 2012 at 06:01:09PM +0100, deadalnix wrote:
Le 21/02/2012 17:56, H. S. Teoh a écrit :
The only thing I added, perhaps, is that instead of problem-specific
conditions, as they appear to have in Lisp, I'm looking at generic
categories of conditions, that you can handle from high-level code
without ever needing to know the specifics of exactly what the condition
is, and yet have the low-level code work it all out once you've made a
decision.


About this, I did some sketching, and I think LISP guys may have
outsmarted you.

Let's consider a transiant condition. You can retry or give up and
throw. But, if you want to retry or not often depend on what went
wrong, no ?

True, and there's nothing to stop you from digging into the details of
the raised Condition if you want to. I did consider implementing
Conditions as some kind of class hierarchy, so that the generic
categories are at the top, underneath Condition, then actual specific
Conditions can extend them. If your handler knows of a specific
Condition, then you can access any pertinent additional info, and make
decisions based on that.

But what I wanted to know was, can you still do something meaningful
even if you knew nothing beyond the top-level generic categories of
Conditions? That way, your handler will still work with new Conditions
that you've never seen before.


T


And here come an idea of mine :

If the Condition has a way to provide the Exception associated. So you can get the hierarchy from the Exception, and you don't need to creat two hierachy of classes just for the bloat.

You'll fond attached some sketching code. What do you think of it ?
import condition;

void main() {
	// Comment switch. Add a slash to switch the piece of code excuted.
	/*
	import core.time;
	auto handlerRegistration = registerRetryBackoffHandler(5, dur!"seconds"(1));
	/*/
	auto handlerRegistration = registerRetryHandler(5);
	//*/
	
	size_t i = 0;
	
	runTransient!({
		import std.stdio;
		writeln("try : ", ++i);
		
		return false;
	})();
}

module condition;

template isCondition(Condition) {
	enum bool isCondition = true;
}

// TODO: refactor this using something from std.container.
private template HandlerStack(Condition) if(isCondition!Condition) {
	void delegate(ref Condition)[] handlers = [];
	
	// static this to avoid : Error: non-constant nested delegate literal expression __dgliteral1
	// Not sure what does it mean and what to do with it.
	static this() {
		handlers ~= (ref Condition c) {
			throw c.exception;
		};
	}
	
	size_t bottom = 0;
	
	alias typeof(handlers[0]) DelegateType;
	
	struct HandlerRegistration {
		@disable this();
		@disable this(this);
		
		this(DelegateType dg) {
			bottom++;
			
			if(handlers.length > bottom) {
				handlers[bottom] = dg;
			} else {
				handlers ~= dg;
			}
		}
		
		~this() {
			handlers[bottom] = handlers[0];
			bottom--;
		}
	}
	
	DelegateType getHandler() {
		return handlers[bottom];
	}
}

auto registerHandler(Condition)(void delegate(ref Condition) dg) if(isCondition!Condition) {
	return HandlerStack!Condition.HandlerRegistration(dg);
}

void raise(Condition)(ref Condition c) if(isCondition!Condition) {
	HandlerStack!Condition.getHandler()(c);
}

// Transient
struct TransientCondition {
	private bool _retry;
	
	// TODO: We should be able to pass a delegate to generate the Exception.
	@property
	Exception exception() {
		return new Exception("transient");
	}
	
	void retry() {
		_retry = true;
	}
}

auto runTransient(alias operation, alias successTest = (bool res) => res)() if(is(typeof(successTest(operation())) == bool)) {
	void fail() {
		auto c = TransientCondition();
		raise(c);
		
		if(!c._retry) throw c.exception;
	}
	
	while(true) {
		try {
			auto res = operation();
		
			if(successTest(res)) {
				return res;
			}
		} catch(Exception e) {
			fail();
			continue;
		}
		
		fail();
	}
}

auto registerRetryHandler(size_t nbRetry) {
	return registerHandler((ref TransientCondition c) {
		if(nbRetry > 1) {
			c.retry();
			nbRetry--;
		}
	});
}

import core.time;
auto registerRetryBackoffHandler(size_t nbRetry, Duration backoff) {
	return registerHandler((ref TransientCondition c) {
		if(nbRetry > 1) {
			import core.thread;
			Thread.sleep(backoff);
			
			c.retry();
			nbRetry--;
			backoff *= 2;
		}
	});
}

Reply via email to