Making fibers enumerable

R

Rahul Kumar

[Note: parts of this message were removed to make it a legal post.]

Hi,

Most of the `Enumerable` methods can be used for fiber based generators if
`Fiber` is made enumerable.

class Fiber
include Enumerable
def each
loop { yield self.resume }
end
end

def fib_gen
Fiber.new {
a, b = 0, 1
while true
Fiber.yield a
a, b = b, a + b
end
}
end

# Find the fibonacci number greater than 1000
fib_gen.find {|x| x > 1000 }
# take first 10 fibonacci numbers
fib_gen.take 10
# take_while numbers are smaller than 1000
fib_gen.take_while {|x| x < 1000 }

If it could be the default behavior, it would be very useful. Some methods
won't be applicable, particularly for non-terminating generators in their
default incarnation viz. drop_while. It can either be re-written to advance
the generator using Fiber#resume or it can be left upto the user to handle
infinite sequences correctly.

If I understand correctly, writing generators was one of the purposes of
fibers. Why can't fibers be made enumerable by default?
 
R

Robert Klemme

Hi,

Most of the `Enumerable` methods can be used for fiber based generators i= f
`Fiber` is made enumerable.

class Fiber
=A0include Enumerable
=A0def each
=A0 =A0loop { yield self.resume }
=A0end
end

def fib_gen
=A0Fiber.new {
=A0 =A0a, b =3D 0, 1
=A0 =A0while true
=A0 =A0 =A0Fiber.yield a
=A0 =A0 =A0a, b =3D b, a + b
=A0 =A0end
=A0}
end

# Find the fibonacci number greater than 1000
fib_gen.find {|x| x > 1000 }
# take first 10 fibonacci numbers
fib_gen.take 10
# take_while numbers are smaller than 1000
fib_gen.take_while {|x| x < 1000 }

If it could be the default behavior, it would be very useful. Some method= s
won't be applicable, particularly for non-terminating generators in their
default incarnation viz. drop_while. It can either be re-written to advan= ce
the generator using Fiber#resume or it can be left upto the user to handl= e
infinite sequences correctly.

If I understand correctly, writing generators was one of the purposes of
fibers. Why can't fibers be made enumerable by default?

I am not sure how that interacts with the original intent of Fibers:
their main purpose is to bring coroutines to Ruby.

http://en.wikipedia.org/wiki/Coroutines

How would your example look if there was another generation that you
wanted to do concurrently? Because for the single threaded generator
case there is already a tool: Enumerator.new. The example in the
documentation even uses Fibonacci Numbers as example. :)

http://www.ruby-doc.org/core/classes/Enumerator.html#M000299

fib_gen =3D Enumerator.new { |y|
a =3D b =3D 1
loop {
y << a
a, b =3D b, a + b
}
}

Now you can do exactly the same as you did with your example

# Find the fibonacci number greater than 1000
fib_gen.find {|x| x > 1000 }
# take first 10 fibonacci numbers
fib_gen.take 10
# take_while numbers are smaller than 1000
fib_gen.take_while {|x| x < 1000 }

For this Fiber would be the wrong tool.

Kind regards

robert

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
P

Peter Zotov

How would your example look if there was another generation that you
wanted to do concurrently? Because for the single threaded generator
case there is already a tool: Enumerator.new. The example in the
documentation even uses Fibonacci Numbers as example. :)

http://www.ruby-doc.org/core/classes/Enumerator.html#M000299

fib_gen = Enumerator.new { |y|
a = b = 1
loop {
y << a
a, b = b, a + b
}
}

Now you can do exactly the same as you did with your example

# Find the fibonacci number greater than 1000
fib_gen.find {|x| x > 1000 }
# take first 10 fibonacci numbers
fib_gen.take 10
# take_while numbers are smaller than 1000
fib_gen.take_while {|x| x < 1000 }

For this Fiber would be the wrong tool.

The Enumerator.new uses Fibers internally. Check enumerator.c in the
sources;
besides that, how would you achieve the needed effect without
coroutines?
 
R

Robert Klemme

The Enumerator.new uses Fibers internally. Check enumerator.c in the
sources;

That's an implementation detail of Enumerator. The point is that the
primary purpose of Fiber is to be able to build concurrency without
preemption but with manual control over when one task yields to
another task. Enumerator.new on the other hand is a tool for
generation of sequences of items which is precisely what the Fibonacci
example is all about. There is no concurrency.
besides that, how would you achieve the needed effect without coroutines?

class X
include Enumerable

Callback =3D Struct.new :code do
def <<(x)
code[x]
self
end
end

def initialize(&code)
@code =3D code
end

def each(&b)
cb =3D Callback.new b
@code[cb]
self
end
end

fib_gen =3D X.new { |y|
a =3D b =3D 1
loop {
y << a
a, b =3D b, a + b
}
}

# Find the fibonacci number greater than 1000
p fib_gen.find {|x| x > 1000 }
# take first 10 fibonacci numbers
p fib_gen.take 10
# take_while numbers are smaller than 1000
p fib_gen.take_while {|x| x < 1000 }

Kind regards

robert


--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
R

Rahul Kumar

[Note: parts of this message were removed to make it a legal post.]

I am not sure how that interacts with the original intent of Fibers:
their main purpose is to bring coroutines to Ruby.

http://en.wikipedia.org/wiki/Coroutines


Right. But aren't semi co-routines(don't send data; just receive) used to
implement generators?


http://www.ruby-doc.org/core/classes/Enumerator.html#M000299

fib_gen = Enumerator.new { |y|
a = b = 1
loop {
y << a
a, b = b, a + b
}
}
Oh, didn't know about this - somehow "The Ruby Programming Language" doesn't
mention it or I missed it. I simply translated my Python code to Ruby.


Thanks for the info Robert.
 
C

Christopher Dicely

Right. But aren't semi co-routines(don't send data; just receive) used to
implement generators?

Ruby fibers can be either semi-coroutines or full co-routines. And
while replacing continuations as the basis for implementing generators
was certainly an important motivation for implementing fibers in Ruby,
that's not the only thing fibers can do, and enumerability may not
make sense for other fibers. So, usually, Enumerator::Generator is
used for generators (though that uses Fiber behind the scenes) and
Fiber is used directly for other use cases.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top