ANN: ResumableFunctions

Hi

C# has a convenient way to create iterators using the yield return statement. The package ResumableFunctions provides the same functionality for the Julia language by introducing the @resumable and the @yield macros. These macros can be used to replace the Task switching functions produce and consume which were deprecated in Julia v0.6. Channels are the preferred way for inter-task communication in julia v0.6+, but their performance is subpar for iterator applications.

using ResumableFunctions

@resumable function fibonnaci(n::Int)
  a = 0
  b = 1
  for i in 1:n-1
    @yield a
    a, b = b, a+b
  end
  a
end

for fib in fibonnaci(10)
  println(fib)
end

Enjoy!

Ben

20 Likes

I apologize for being late to the ResumableFunctions vs PyGen party, but for me, the only time I really needed PyGen (because there is no other simple alternative) is when making recursive generators, basically traversing tree-like structures without having to generate it all a priori. So I tested the following pieces of codes in Julia using PyGen and ResumableFunctions and in Python and here are the results:

a = [[1,2,3],[4,5,6]]
@resumable function g(x)
	if isa(x,Number)
		@yield x
	else
		for i in x
			for j in g(i)
				@yield j
			end
		end
	end
end

for i in g(a)
	println(i)
end

#=
1
false
2
false
3
false
nothing
4
false
5
false
6
false
nothing
nothing
=#


a = [[1,2,3],[4,5,6]]
@pygen function f(x)
	if isa(x,Number)
		yield(x)
	else
		for i in x
			for j in f(i)
				yield(j)
			end
		end
	end
end

for i in f(a)
	println(i)
end

#=
1
2
3
4
5
6
=#

import numbers
a = [[1,2,3],[4,5,6]]
def f(x):
	if isinstance(x, numbers.Number):
		yield x
	else:
		for i in x:
			for j in f(i):
				yield j

for i in f(a):
	print i

"""
1
2
3
4
5
6
"""

Clearly ResumableFunctions generates extra entries not in the other 2, can that be treated?

Hi @mohamed82008

I can explain the behaviour:

  • the extra false entries are a side effect of the transformation of a for loop in a while loop
  • the extra nothing is the normal return of the function (this is also the default behaviour in C#)

Can you open an issue in the ResumableFunctions repository?
The first one can be directly solved. The second one I have to look at the Iterator interface to see if I can fix it.

This is really a very nice use case and I hope I can find a solution;)

Ben

Interesting, I will open an issue then. Thanks for your effort btw, that’s an awesome piece of work!

A complete explanation of this weird but logical behaviour can be found in this issue.
The correct sequence can be created by 3 minor modifications:

using ResumableFunctions

@resumable function g(x)
    if isa(x,Number)
        return x # If x is a number the @resumable function is only called once
    else
        for i in x
            for j in g(i)
                j == nothing || @yield j # No explicit return is defined, so we have to eliminate the default returns
            end
        end
    end
end

a = [[1,2,3],[4,5,6]]

for i in g(a)
    i==nothing || println(i) # Eliminate the default return
end
1 Like

Cool, thanks!