The "lazy" command is now part of CritLib [L1 ]
NEM 2008-05-24: Where? I don't see it in my install.
This demo is a self-contained package which supports creating values that are evaluated lazily, i.e. on the first time they are used. More exactly, "lazy" constructs a Tcl_Obj* value with no string representation.
The first time the value is used, a previously specified command is run, the result of which is then used as string representation. The fact that such lazy evalution was applied is totally hidden, and one-shot only.
An example:
proc readfile {filename} { return [read [open $filename]] } file delete myfile.txt set grabit [lazy readfile myfile.txt] exec date myfile.txt puts $grabit
The result will show as a timestamp, even though that information didn't exist at the time when $grabit was defined.
There are numerous uses for this "delayed processing" mechanism, but I'm not yet sure it is working reliably. There have been some unexplained interactions (even though interpreter state *is* saved and restored).
One known weakness, is that the code executed lazily may never throw an error (this makes the above example a bad one, btw). That may prove to be a big show stopper. There simply is no logical place to handle such an error condition.
A related issue, is that the delayed call stashes away a copy of its Tcl interpreter, because even though it should not alter it, execution does require having such an interpreter context to work in.
Source:
package require lazy proc yell {args} { puts stderr "YELL $args"; return $args } proc lazy_try {} { catch {lazy -peek yell} err puts "err = $err" puts [lazy yell bingo] set a [lazy yell one two three] puts "peek = [lazy -peek $a]" puts "eval = $a" puts "eval = [llength $a]" set ::errmsg "" puts "thrown = [lazy error bang!]" puts "caught = [lazy catch {error boom!} errmsg]" puts "errmsg = $::errmsg" set ::errorInfo "" set a [lazy error dontlook!] puts "peek a = [lazy -peek $a]" puts "errorInfo <[join [split $::errorInfo \n] { - }]>" string length $a puts "errorInfo <[join [split $::errorInfo \n] { - }]>" puts "eval a = $a" set a "" set b [catch {lazy error yes?} a] puts "errorInfo <[join [split $::errorInfo \n] { - }]>" puts "a = $a, b = $b" global one trace add variable one {read write unset} [list yell trace] set one(1) un puts $one(1) unset one set two [lazy yell deux] puts "two #0" puts "peek [lazy -peek $two]" puts "two #1 $two" puts "two #2 $two" } lazy_try
Output (SuSE 7.1 Linux, PIII/650):
err = not a lazy object YELL bingo bingo peek = yell one two three YELL one two three eval = one two three eval = 3 thrown = bang! caught = 1 errmsg = boom! peek a = error dontlook! errorInfo <> errorInfo <dontlook! - while executing - "error dontlook!"> eval a = dontlook! errorInfo <dontlook! - while executing - "error dontlook!"> a = yes?, b = 0 YELL trace one 1 write YELL trace one 1 read un YELL trace one {} unset two #0 peek yell deux YELL deux two #1 deux two #2 deux
See also Streams - Deferred evaluation
See also Lazy lists.
I release simple 'lazy' functionality via new Tcl_Obj type, see https://github.com/acanta/tcl-sniplet
Sample: =====
load ./sniplet.so set ls { 1 2 3 } set ret [ lazy {ls} { puts "lazy execution"; return [ llength $ls ] } ] lappend ls 4 puts "ls now=$ls" puts "ret=$ret"
=====
Result should be:
ls now=1 2 3 4 lazy execution ret=3