Forum: Ruby lazy.rb 0.2

Announcement (2017-05-07): is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see and for other Rails- und Ruby-related community platforms.
mental (Guest)
on 2005-12-11 07:52
(Received via mailing list)
Now that I'm done asking stupid questions about iterators, it's time for
another release of lazy.rb!


lazy.rb is a library which provides lazy evaluation for Ruby.  It allows
you to optimistically defer computations until they are required.

These deferred computations are represented by "promises":

 prom = promise { 2 * 3 }
 p prom #=> #<Lazy::Promise 
 result = demand prom
 p result #=> 6

A promise is only evaluated once, after which it becomes essentially
indistinguishable from its result object:

 p prom       #=> 6
 p prom.class #=> Fixnum

In fact, you can simply use the promise directly and it will be
evaluated for you automatically:

 prom = promise { 8 + 1 }
 num = prom * 2
 p num #=> 18
 p prom #=> 9

There's one caveat; Ruby always considers promises, evaluated or not, to
be true.  If you are testing a result for truth, you must unwrap the
result with demand.

 prom = promise { false }
 demand prom # force evaluation
 p prom #=> false

 puts "wait, it's true?" if prom #=> wait, it's true?

 puts "no, false.  much better." \
   unless demand( prom )
   #=> no, false.  much better.

Conveniently, calling demand on an already evaluated promise (or even on
a regular object) is harmless.

 p demand( 6 ) #=> 6


Kernel#force has been renamed to Kernel#demand, though Kernel#force
still exists as an alias.  I'm still wavering on the final name.
Feedback is welcomed.

I've also incorporated a very nice suggestion from Tom P., so that
Object#inspect no longer forces the evaluation of a promise.  This makes
promises much easier to work with in irb.

The biggest new feature is an API for lazy streams.  Lazy streams are
linked lists of deferred computations.  Iterating over the list forces
its evaluation, cell by cell.  They can be used for many purposes,
including a substitute for generators or threads.

As linked lists, each cell in the stream (Lazy::Cons) has two
attributes: first, and rest.  first is the value in that cell, and rest
is a reference to the tail (remainder) of the list.

There is also a Lazy::Stream class, which wraps a lazy stream and
implements Enumerable.

The API tries to make both imperative and "pure" stream creation easy.

For example:

 # create a stream of lines from stdin
 stdin_lines = Lazy::generate_stream do |tail|
                 line = $stdin.gets
                 if line
                   [ line, tail ]

 # create a stream of Fibbonacci numbers
 def make_fibs( a=1, b=0 )
   Lazy::cons_stream do
     sum = a + b
     [ sum, make_fibs( b, sum ) ]
 fibs = make_fibs

You can use them directly, as linked lists:

 # echo lines from stdin
 iter = stdin_lines
 while demand( iter )
   puts iter.first
   iter =

...or via the Enumerable interface:

 # print the Fibbonacci numbers up through 100
 enum = fibs )
 enum.each do |n|
   puts n
   break if n > 100

The lazy streams API is still experimental and missing some important
functionality (take and drop, for example), but it's already useful at
this point and I'd like to get some feedback.


I've got a small web site here:

lazy.rb is available for download as both a tarball and a gem.  This is
my first real gem, so let me know if you have any problems with it.

There's also a link to the API documentation, but sadly nothing in the
way of a tutorial yet.

I'll eventually also start publishing my git repository.  I keep meaning
to get around to it...

ezra (Guest)
on 2005-12-11 10:04
(Received via mailing list)
On Dec 10, 2005, at 9:48 PM, MenTaLguY wrote:

> Now that I'm done asking stupid questions about iterators, it's
> time for
> another release of lazy.rb!
> -mental


	This is very interesting stuff! Thanks you for your work on it.


-Ezra Z.
Yakima Herald-Republic Newspaper
This topic is locked and can not be replied to.