Thursday, 26 April 2018

Reentrant Function

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