JuliaDynamics / ResumableFunctions.jl

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

Issue with functions that take varargs #11

Closed oxinabox closed 6 years ago

oxinabox commented 6 years ago

Hi, I am having trouble using ResumableFunctions.jl to implement Interleave. I have does so using Channels, and it works fine. but ResumableFunctions.jl is just returning basically the iterators I feed my function, not there elements. Am I doing something wrong?

Compare:

function interleave_ch(xs...)
    states = Base.start.(collect(xs))
    Channel(csize=256, ctype=Union{eltype.(xs)...}) do c       
        while true
            alldone = true
            for ii in eachindex(states)
                if !Base.done(xs[ii], states[ii])
                    alldone=false
                    val, states[ii] = next(xs[ii], states[ii])
                    put!(c, val)
                end
            end
            alldone && break
        end
    end

end

which has: collect(Iterators.take(interleave_ch(100:300:900, 'a':'z'), 20)) returning: Union{Char, Int64}[100, 'a', 400, 'b', 700, 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q']"

Vs:

@resumable function interleave_rf(xs...)
    states = Base.start.(collect(xs))
    while true
        alldone = true
        for ii in eachindex(states)
            if !Base.done(xs[ii], states[ii])
                alldone=false
                val, states[ii] = Base.next(xs[ii], states[ii])
                @yield val
            end
        end
        alldone && break
    end   
end

which has collect(Iterators.take(interleave_rf(100:300:900, 'a':'z'), 20)) returning Any[100:300:700, 'a':1:'z', nothing]

Which I just don't think is right. I think the output should be the same. The nothing comes from #2, so I am not worried about that. But to me it looks like this function should perform interleave. I may have messed something up though.

oxinabox commented 6 years ago

I wanted to include this as an example in: http://white.ucc.asn.au/2017/11/18/Lazy-Sequences-in-Julia.html

I can still go back an add it later of-course.

oxinabox commented 6 years ago

Ah, I have it.

ResumableFunctions doesn't handle functions with varargs right.

@resumable function foo(xs...)
    @yield xs[1]
    @yield xs[2]
end

collect(foo(3,4))

Output:

BoundsError: attempt to access ((3, 4),)
  at index [2]

Stacktrace:
 [1] getindex(::Tuple{Tuple{Int64,Int64}}, ::Int64) at ./tuple.jl:21
 [2] (::##974)(::Void) at /home/wheel/oxinabox/.julia/v0.6/MacroTools/src/utils.jl:132
 [3] _collect(::UnitRange{Int64}, ::##974, ::Base.HasEltype, ::Base.SizeUnknown) at ./array.jl:442
 [4] collect(::##974) at ./array.jl:431
 [5] include_string(::String, ::String) at ./loading.jl:522
BenLauwens commented 6 years ago

Hi

The problem is the splatting of the varargs. I have implemented a fix in the master branch. I added a test and it seems to work... Can you test that this solves your problem. Thanks!

Ben

BenLauwens commented 6 years ago

Hi

I tested your interleave example and it seems fine. So I will release a new version of ResumableFunctions, so that you can update your blog;) Regards

Ben

oxinabox commented 6 years ago

Done and looking good