package require punk #https://www.youtube.com/watch?v=26jVysJHB-s #based on Monads in Python: Why and How? #some function timer proc xxxpipet {subcommand} { return [list % val $subcommand |sub> { if {![info exists input]} { set input "" } else { set input } } |input> {clock micros} |start> {{*}$sub {*}$input} |res> {expr [clock micros] - $start} |t> {list $res $t} <input| ] } proc pipet {subcommand} { return [list % list $subcommand |sub/0,input/1> \ {clock micros} |start> {{*}$sub {*}$input} |res> {expr [clock micros] - $start} |t> {list $res $t} <input| ] } pipeset fast % {expr {$x + 1}} <x| pipeset slow % {after 100} |> {expr {$x + 1}} <x| pipeset slow2 % {after 150} |> {expr {$x + 2}} <x| #naive chaining of time-wrapped commands. #Requires unpacking/matching at each stage of pipeline and summing at end. % .= val 2 |> .= {{*}[pipet $::fast] $data} |d@0,t@1> {{*}[pipet $::slow] $d} |d@,t2@> {{*}[pipet $::slow2] $d} |d@,t3@> {list $d [expr {$t + $t2 + $t3}]} #method 2 - abstract away the extraction and summation in a bind function # x2, t = bind(bind(fast(1), slow), slow2) # % bind=% {list $vt $f} |v@0/0,t@0/1> {{*}$f $v} |r2@,t2@> {list $r2 [expr {$t + $t2}]} <vt@,f@| pipeset bind % {list $vt $f} |v@0/0,t@0/1> {{*}$f $v} |r2@,t2@> {list $r2 [expr {$t + $t2}]} <vt@,f@| # requires nested binds .. a bit messy {*}$bind [{*}$bind [{*}[pipet $::fast] 1] [pipet $::slow]] [pipet $::slow2] #we can neaten it up with tcl aliases, but we still don't have a straightforward pipeline so it will get messier with more operations. alias t_fast {*}[pipet $fast] alias t_slow {*}[pipet $slow] alias t_slow2 {*}[pipet $slow2] alias bind {*}$bind bind [bind [t_fast 1] t_slow] t_slow2 #now with oo approach catch {timedvalue destroy} oo::class create timedvalue { variable o_value variable o_time constructor {value {time 0}} { set o_value $value set o_time $time } method value {} {return $o_value} method time {} {return $o_time} method bind {f} { set timed_value [{*}$f $o_value] set new_value [$timed_value value] set new_time [expr {$o_time + [$timed_value time]}] return [timedvalue new $new_value $new_time] } } #we need a new timing wrapper that returns an object proc pipet2 {subcommand} { return [list % list $subcommand |sub/0,input/1> \ {clock micros} |start> {{*}$sub {*}$input} |res> {expr [clock micros] - $start} |t> {timedvalue new $res $t} <input| ] } alias o_fast {*}[pipet2 $fast] alias o_slow {*}[pipet2 $slow] alias o_slow2 {*}[pipet2 $slow2] tvresult.= \ .= o_fast 1 \ |o> {$o bind o_slow} \ |o> {$o bind o_slow2} puts "tvresult value: [$tvresult value]" puts "tvresult time: [$tvresult time]" #timedvalue class is a monad