JuliaDynamics / ResumableFunctions.jl

C# style generators a.k.a. semi-coroutines for Julia.
Other
160 stars 19 forks source link

Base.intersect doesn't work for resumable functions #107

Open MarkNahabedian opened 1 week ago

MarkNahabedian commented 1 week ago

Though Base.union works with the iterators produced by @resumable, Base.intersect does not:

using ResumableFunctions

@resumable function onetwothree()
    @yield 1
    @yield 2
    @yield 3
end

intersect(onetwothree(), [2])

ERROR: @resumable function has stopped!
Stacktrace:
  [1] error(s::String)
    @ Base .\error.jl:35

The below suggestion is untested.

First we need a common supertype for all resumable functions:

abstract type ResumableFunction end

The @resumable macro would need to be modified such that the struct it defines inherits from that supertype.

Then, something like

function Base.intersect(iter1::ResumableFunction, iters...)
    found = Dict()
    for v in iter1
        found[v] = 1
    end
    for iter in iters
        for v in iter
            if haskey(found, v)
                found[v] += 1
            end
        end
    end
    need = 1 + length(iters)
    for (v, count) in found
        if count != need

        end
    end
    keys(found)
end

This doesn't quite adhere to the documentation for Base.intersect, which says that the result should be of the same type as the first argument. If the first argument is an iterator then presumably returning an iterable is sufficient. In any case, its an improvement over Base.intersect not workiing if the first argument is a ResumableFunctions iterator.

Krastanov commented 1 week ago

Thanks for reporting this, it is indeed a bug within ResumableFunctions that we should fix.

Currently I am a bit uncomfortable with the proposed solution. It would indeed make intersect work, but there are probably other methods on iterables that will continue failing. I hope there is a solution where we can just make the default intersect implementation work (so that we do not play whack-a-mole every time there is a new function to worry about). After all, the default collect works fine.

Which is also a quick workaround until this is fixed:

intersect(collect(resumable_f), some_other_iterator)