The wikipedia article about coroutines makes an interesting read. In simple terms we can say that a coroutine is a function with multiple entry points, so that it can exit and start its execution later in a different point.
This should sound familiar. You could create a reentrant function by means of a closure, that would keep a state dictating what to do in the ensuing call. Both in C# and JavaScript the yield and await keywords allow us to create reentrant functions (well, await is a bit different, we could say that the function is reentring itself). Indeed, the terminology used in JavaScript, generator, is in accordance with the Computer Science nomenclature used to define a limited type of coroutines.
The thing is that invoking a generator function (javascript) or an iterator method (C#) return a generator/IEnumerable-IEnumerator object on which we will invoke the next or MoveNext methods. So we don't really have a reentrant function, but invocations on the same method of an object that maintains state. If we really want the effect of invoking a reentrant function we can easily leverage a generator/Iterator. I'm using C# iterators for my samples, it would be basically the same in JavaScript.
We can define a Factory method that will return a reentrant function (delegate). This factory method contains a local function defining an iterator and creates a closure that manages the iteration.
public static Func<string> CreateMessageProvider<T>()
{
IEnumerator<string> messageProvider(){
yield return "Bonjour";
yield return "Hola";
yield return "Hello";
}
IEnumerator<string> enumerator = messageProvider();
return () => {
if (!(enumerator.MoveNext())){
throw new Exception("Finished");
}
return enumerator.Current;
};
}
//main
coroutine = CreateMessageProvider<string>();
Console.WriteLine(coroutine());
Console.WriteLine(coroutine());
Console.WriteLine(coroutine());
We can generalize the above code to create a reentrant function from any provided IEnumerable:
public static Func<T> CreateCoroutine<T>(IEnumerable<T> enumerable)
{
IEnumerator<T> enumerator = enumerable.GetEnumerator();
return () => {
if (!(enumerator.MoveNext())){
throw new Exception("Finished");
}
return enumerator.Current;
};
}
No comments:
Post a Comment