#slow - but basically works. Needs pipeline compilation to tcl script to speed up? funcl?
package require punk
#todo - pinned vars.. what scope? add a facility to bake values into proc body? (what equivalent is there for pipelines not bound to a proc scope? apply?)
#if a fun is intended to bind in the caller's scope - then we just need a way to return the fun contents as a runnable pipeline
# - conflict between build-time and run-time pinned vars? Is this really 2 separate concepts?
# - using a pin to help build up the pattern vs using the pin for pipelined/context data?
# - unpinned vars are clearly required in the implementation bodies - which is 'pipeline' scope only.. and limited to the running clause.
#pipefun::setvar ?
#consider oo version that uses properties of the object as the pinned vars in the pattern-spec?
#pinned vars are important to be able to pass more complex values than are allowed by the pattern mini-lang e.g paths /etc (or is that a misuse???)
#what does a pin on the argspec <^var| even mean? other vars in <| are pure pipeline scope
# pins in intermediate pipes.. e.g |^somevar@1> could be used as sanity checks for pipeline-scope vars.. but is there a real need/usecase?
namespace eval pipefun {
variable funs [dict create]
}
#note this isn't a 'standard' closure in the sense that the captured vars aren't modifiable - a coro or object could be used to hold state if that is required.
proc pipefun::closure0 {body}{
set binding {}
foreach v [uplevel1 info locals]{
upvar1$v var
if{[info exists var]}{
lappend binding [list$v$var];#values captured as defaults for apply args.
#experiment with match but return error instead of ok {result xxx}
fun age a {
%switchresult.= pipeswitch {
#puts "testing-arguments: $a"
if{![info exists a]}{
error[list ignore no-arg]
}
pipecase .= val $a|a@0>\
0.={
string is integer -strict $a
}|>{
puts stderr "agemustbeaninteger"
val"badarg'$a'-mustbeaninteger"
}
}
#puts stdout ">>> $switchresult"
%status@@/@0.= val $switchresult
%contents@1.= val $switchresult
puts stdout ">>>$switchresult"
#an ordinary return value (extracted from @@ok/result above) will get wrapped in 'ok {result <value>}' which can be confusing if the result is supposed to indicate a fault
#return $status
#even worse if we return switchresult as we would get a double @@ok/result
#pipeswitch can return @@ok/result or @@error
# we could use something like:
#if {[$status eq "error"]} {...}
# or
#if {$status eq "ok"} {...}
# or just use error "switcherror ..." directly in the above pipecase, which would propagate through as an error
#wrapping in own error-bag doesn't really help as the outer ok wrapping will still get applied
#return [list error {reason "bad arg"}]
#raising a normal error in the above pipeswitch or anywhere in the fun results in an @@error/reason *return value*
#(as opposed to an @@error/mismatch *return value* for a failed binding)
#i.e later implementations will still be run as this will still just be a non-match
#error "$contents"
#will work to raise an error, but bad form to fake a pipesyntax error
#error "pipesyntax bad-arg"
#to cause pipewitch above this to raise an error and stop processing cases..
#use 'switcherror' or 'funerror' as first element of a list e.g
#error [list funerror [list reason "$contents"]]
#attempting to raise a normal error in the above switch - but then a bad match here.. will only cause this implementation to be a non-match
# and so other implementations will still be run
#return [@@ok/result.= val $switchresult]
#in this example - we want to force a terminal error (see switcherror above) or a terminal *result* that isn't wrapped in 'ok',
#but only if we *don't* have a mismatch.
#i.e we require an @@error/mismatch to proceed to other implementations
#all good - simulate an @@error/mismatch - or raise an 'ignore' error so that this implementation is considered to be unmatched and we fall through to others.
#puts stdout "=ispipematch true"
error[list ignore [list argcheck passed]]
#error "binding mismatch"
}else{
#puts stdout "=ispipematch false"
#error [list resultfunerror $contents]
error[list funerror $contents]
}
}
fun age 1{puts"welcome!"}
fun age 18{puts"yay!"}
fun age 42{
error"Testthaterrorisraised."
}
fun age ''/0{puts"enteranage,andoptionallyamessage";return"no-value-supplied"}
fun age a,100{puts"congrats!";return"congrats-$a"}
fun age b {puts"cool";return"cool-$b"}
#fun age a,3+/# {puts stderr "too many args"; return}