[NEM] 2009-07-31: Here's a simple interactive [tclsh]-style application, with the difference that it runs inside a [coroutine]. This allows you to have complex event-driven interactions appear as if they are happening in a blocking, non-event-driven manner. For example, you can have [Tk] [GUI] applications responding to events while you are apparently in a blocking [HTTP] request. ====== # corotcl -- # # A coroutine-enabled tclsh # package require Tk proc lambda {params body args} { list ::apply [list $params $body] {*}$args } proc prompt p { puts -nonewline "$p " flush stdout fileevent stdin readable [lambda return { $return [gets stdin] } [info coroutine]] yield } proc get-command {} { set cmd [prompt %] while {![info complete $cmd]} { append cmd \n [prompt >] } return $cmd } proc repl {} { while 1 { set cmd [get-command] set code [catch { uplevel #0 $cmd } result opts] if {$code == 1} { puts [dict get $opts -errorinfo] } else { puts $result } } } coroutine main repl #vwait forever ;# if no Tk ====== An example of use. First, we bring up a simple Tk interface to check that everything keeps running in the background: ====== % pack [button .b -text Test -command {puts Test}] .b ====== Testing this will show that it does print "Test" in the console when clicked. So far, so normal. Now, we create a coroutine-enabled version of [http::geturl]: ====== % package require http 2.7.2 % proc fetch url { > http::geturl $url -command [info coroutine] > yield > } ====== We can now use this to fetch data from the web, just like the usual geturl. The difference is that this works in the background: you can still click on the Tk button and it will still print to the console even while the fetch is still in progress: ====== % set t [fetch http://wiki.tcl.tk/4] Test Test Test ::http::1 ====== There we go: an interactive Tcl interpreter that is fully coroutine enabled. Next step? [Futures]? ---- '''[CMcC] - 2009-07-31 11:36:54''' Very cool. Here's a variant which uses a TclOO object called Shell so one can have multiple of these constructed with different chans as i/o. # Shell -- a coroutine enabled tcl evaluator # # From corotcl http://wiki.tcl.tk/24060 package require TclOO namespace import oo::* class create Shell { constructor {{in stdin} {out ""}} { if {$out eq ""} { if {$in eq "stdin"} { set out stdout } else { set out $in } } chan configure $out -buffering line proc prompt {in out p} { puts -nonewline $out "$p " chan flush $out chan event $in readable [list ::apply {{return in} { $return [gets $in] }} [info coroutine] $in] return [yield] } proc repl {in out} { while {1} { set cmd [prompt $in $out %] while {![info complete $cmd]} { append cmd \n [prompt $in $out >] } switch -- [catch { uplevel #0 $cmd } result eo] { 1 { chan configure $out -buffering none puts $out [dict get $eo -errorinfo] chan configure $out -buffering line } 2 - 3 { break } 4 { continue } default { puts $out $result } } } chan event $in readable {} return $result } coroutine [self]_CORO repl $in $out } } if {[info exists argv0] && ($argv0 eq [info script])} { Shell new vwait forever } <>Event Loop