The Laziest Variable in the Whooole World

D

Daniel Nugent

Hello fellow Rubyists,

Something got to niggling me in the back of my mind last week during a
lecture in my Programming Languages class about laziness in functions.
Basically, the point is that functions that don't evaluate until they
need to are cool.

We can be pretty lazy right now with code generators that will let us
fake infinite series and stuff like that, but I think we can be even
LAZIER if we put our minds to it.

Along these lines, I came up with this:

class Lazy
=20
#DESTROY ALL METHODS! DESTROY ALL INSTANCE METHODS!
instance_methods.each { |m| undef_method m unless m =3D~ /^__/ }

def initialize(switch =3D :eek:ne_eval, &block)
@code_block =3D block
@mode =3D switch
end
=20
def method_missing(symbol, *args)
if @mode =3D=3D :eek:ne_eval
@real_obj ||=3D @code_block.call
@real_obj.__send__(symbol, *args)
else
@code_block.call().__send__(symbol, *args)
end
end
end

What this does is let us wait until a value is really, actually needed
before the code that would provide it is evaluated. This is nice if
you wanted to put off that expensive retrieval operation until it was
actually needed, if at all, and if you're tricky about it, you can get
non-deterministic results just like the bastiche you want to be (just
set the switch to something besides :eek:ne_eval, now the block is called
ALL the time!).

With a little bit of tricksyness, we can use this to let us turn
regular variables into dataflow variables, which, instead of causing
all sorts of errors because they're not ready yet, merely make the
program wait until they're darned well not nil (or whatever condition=20
you happen to need). For example:

#Dataflow example
foo =3D Lazy.new do
while $data_flow_variable.nil?
end
$data_flow_variable
end

bar =3D Thread.new {print foo}
baz =3D Thread.new {sleep(3);$dfv =3D "Hello World, sorry I'm late"}

bar.join
baz.join

This is, of course, trivial, but it demonstrates how you might employ
data flow for fun and profit (Especially if you could abstract
laziness and dataflowism into functions that did all the boring object
creation work for you, which, of course, you could).

The question, of course, is where are the gaps here? Where does this
idea just fall apart and spew, SPEW all over the floor? I'm
reasonably sure you're all much more masterful at Ruby than I am and
stand a better chance at figuring out what could explode (I personally
expect that assignment will botch it up, but I can't be sure.) And,
if explosion is imminent, can it be fixed?

Thanks for reading!

PS: Thanks to Aria and binary42 for smacking me around a little and
pointing me to {|one, step, back|} and the BlankSlate idea there.
--=20
-Dan Nugent
 
D

Daniel Nugent

Whoops, you're right, should have used ! defined? as the conditional
test instead. (Not that you can't use whatever conditional that
pleases you)

Also, there's a mistake in the above code, $data_flow_variable and
$dfv are supposed to be the same variable. (That'll teach me to not
version control my discussion group email).

If there's a better way of doing this, I'd like to hear about it. The
reason I decided to go with this is that you can do the lazy
evaluation and get data flow as a bonus (incidentally, I understand
disgruntlement with the tight loop, I am, however, unaware of a better
way).

About the reverse-event registration thing: Yeah, it's kind of like
RSS syndication like that.

Maybe you could put a sleep in the loop? I dunno.

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
=20
It's an interesting idea but it may be considered a slight
perversion of the language. I would expect that dataflow would be
handled better with pipes or sockets or shared objects or the like.
~ At the very least, run your example with -w :)
=20
Still, it's kind of an interesting little reverse-event registration
bit of code (i.e. the source doesn't broadcast to the sinks, but the
sinks simply read from the source when it becomes available -- i'm
not happy with a busy-wait loop tho).
=20
Regs,
D
=20
Daniel Nugent wrote:
| Hello fellow Rubyists,
|
| Something got to niggling me in the back of my mind last week during a
| lecture in my Programming Languages class about laziness in functions.
| Basically, the point is that functions that don't evaluate until they
| need to are cool.
|
| We can be pretty lazy right now with code generators that will let us
| fake infinite series and stuff like that, but I think we can be even
| LAZIER if we put our minds to it.
|
| Along these lines, I came up with this:
|
| class Lazy
|
| #DESTROY ALL METHODS! DESTROY ALL INSTANCE METHODS!
| instance_methods.each { |m| undef_method m unless m =3D~ /^__/ }
|
| def initialize(switch =3D :eek:ne_eval, &block)
| @code_block =3D block
| @mode =3D switch
| end
|
| def method_missing(symbol, *args)
| if @mode =3D=3D :eek:ne_eval
| @real_obj ||=3D @code_block.call
| @real_obj.__send__(symbol, *args)
| else
| @code_block.call().__send__(symbol, *args)
| end
| end
| end
|
| What this does is let us wait until a value is really, actually needed
| before the code that would provide it is evaluated. This is nice if
| you wanted to put off that expensive retrieval operation until it was
| actually needed, if at all, and if you're tricky about it, you can get
| non-deterministic results just like the bastiche you want to be (just
| set the switch to something besides :eek:ne_eval, now the block is called
| ALL the time!).
|
| With a little bit of tricksyness, we can use this to let us turn
| regular variables into dataflow variables, which, instead of causing
| all sorts of errors because they're not ready yet, merely make the
| program wait until they're darned well not nil (or whatever condition
| you happen to need). For example:
|
| #Dataflow example
| foo =3D Lazy.new do
| while $data_flow_variable.nil?
| end
| $data_flow_variable
| end
|
| bar =3D Thread.new {print foo}
| baz =3D Thread.new {sleep(3);$dfv =3D "Hello World, sorry I'm late"}
|
| bar.join
| baz.join
|
| This is, of course, trivial, but it demonstrates how you might employ
| data flow for fun and profit (Especially if you could abstract
| laziness and dataflowism into functions that did all the boring object
| creation work for you, which, of course, you could).
|
| The question, of course, is where are the gaps here? Where does this
| idea just fall apart and spew, SPEW all over the floor? I'm
| reasonably sure you're all much more masterful at Ruby than I am and
| stand a better chance at figuring out what could explode (I personally
| expect that assignment will botch it up, but I can't be sure.) And,
| if explosion is imminent, can it be fixed?
|
| Thanks for reading!
|
| PS: Thanks to Aria and binary42 for smacking me around a little and
| pointing me to {|one, step, back|} and the BlankSlate idea there.
=20
=20
- --
Derek Wyatt - C++ / Ruby / Unix Programmer
http://derekwyatt.org
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (MingW32)
=20
iD8DBQFDIEZLwwHFeC88e2IRAhdEAJ9xcxubLshcNKJpyV8TWZpKavPxOACfYZPE
onAKgZDEDaJ0PJlAkY+Mbbc=3D
=3Dt54M
-----END PGP SIGNATURE-----
=20
=20


--=20
-Dan Nugent
 
D

Daniel Nugent

How about using Thread.pass to avoid sitting in the loop while your
resources could be better spent elsewhere?

UPDATED SAMPLE:

class Lazy

#DESTROY ALL METHODS! DESTROY ALL INSTANCE METHODS!
instance_methods.each { |m| undef_method m unless m =3D~ /^__/ }

def initialize(switch =3D :eek:ne_eval, &block)
@code_block =3D block
@mode =3D switch
end

def method_missing(symbol, *args)
if @mode =3D=3D :eek:ne_eval
@real_obj ||=3D @code_block.call
@real_obj.__send__(symbol, *args)
else
@code_block.call().__send__(symbol, *args)
end
end
end


#Dataflow example
foo =3D Lazy.new do
while ! defined? $dfv
=09Thread.pass
end
$dfv
end

bar =3D Thread.new {print foo}
baz =3D Thread.new {sleep(3);$dfv =3D "Hello World, sorry I'm late"}

bar.join
baz.join

--=20
-Dan Nugent
 

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

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,822
Latest member
israfaceZa

Latest Threads

Top