Skip to content

Instantly share code, notes, and snippets.

@timrandg
Created September 17, 2011 22:09
Show Gist options
  • Save timrandg/1224423 to your computer and use it in GitHub Desktop.
Save timrandg/1224423 to your computer and use it in GitHub Desktop.
why do fibers do this?
#I am curious why a fiber returns the arguments when there are no more yield statements left to evaluate--see below. Why not return nil?
fiber = Fiber.new do |first, second|
Fiber.yield first + 30
Fiber.yield first + 20
end
p fiber.resume 10, 20 # => 40
p fiber.resume 10, 20 # => 30
p fiber.resume 10, 20 # => [10, 20]
@rkh
Copy link

rkh commented Sep 17, 2011

Because they return the return value of the blog. Fiber.yield returns whatever you pass to resume. So you can do this:

fiber = Fiber.new do
  puts Fiber.yield
  "done"
end

fiber.resume
puts fiber.resume("ready?")

@timrandg
Copy link
Author

Starting to see it more clearly now. I made an attempt to explain how I think it works below...is it accurate? Tim

class Fiber
  alias_method :pass_in, :resume
  class << self
    alias_method :pass_out, :yield
  end
end


fiber = Fiber.new do |a,b,c|
  it, is = Fiber.pass_out a#fiber_1 runs to here, returning 10. On next resume (pass_in), we start from here also.
  t = Fiber.pass_out it, c #fiber_2 runs to here
  #fiber_3 runs to here
end

ret_1 =  fiber.pass_in(1000,2000,3000)
ret_2 =  fiber.pass_in('f', 'j')
ret_3 =  fiber.pass_in(1,2,3) #only returned because there was nothing left in the fiber beyond this fiber stop
p ret_1 # => 1000
p ret_2 # => ['f', 3000]
p ret_3 # => [1,2,3]

#I have a hard time ignoring the more commonly encountered 'yield' associated with blocks. Yield in the
#context of fibers trips me up. It seems to do something different here, so I renamed it pass_out.
#Likewise, I aliased 'resume' as pass_in. So, storing the new Fiber as fiber, we call fiber.pass_in 
#(= fiber.resume). This is the first call of this fiber, and the arguments get set to a,b,c as you would
#expect for a block. The code continues to run to the next line. Here the variable it and is are going 
#to be set. To do that we need to know what Fiber.yield (pass_out) evaluates to. However, as soon 
#as we call Fiber.yield(arg), the arg gets sent out, and the focus is passed out to the caller. This 
#means ret_1 now gets arg, which is a (=1000). When we go to the next line we call fiber.pass_in('f', 'j'). 
#Now, the Fiber.pass_out receives the incoming 'f' and 'j' argumentsa and the it variable is set to'f', 
#while the 'j' variable is assigned to is. The code continues until Fiber.pass_out(it, c) is read, now the
#focus is back #outside the Fiber block and ret_2 is assigned ["f", 3000]. When the next 
#fiber.pass_in(1,2,3) is read the focus goes back to the Fiber.pass_out point and the (1,2,3) args come in.
#However, this statement 1,2,3 #is the last line of code and is therefore implicitly returned and assigned 
#to ret_3. 

#In reading Fibers, one can imagine a marker '_' where the Fiber.yield statement is, and imagining its
# argument to get passed out. Now the _ waits at that position for a resume call. When resume is called
# the args are brougt in at the point of the _ and the code continues to the next Fiber.yield.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment