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 $grabitThe 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_tryOutput (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 deuxSee 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