Playing newLISP

In Playing newLISP, Tcl takes on some of the features of that language.

One unique feature of newLISP is that the definition of a function can be modified during the evaluation of the function. stepeval implements this behaviour, with the small twist that it discards the commands of the script as it evaluates them. This shifts some of the management overhead into stepeval, allowing the Tcl examples below to be more concise than their newLISP counterparts.

#requires scriptSplit from wiki.tcl.tk/cmdSplit
proc evaluator {procns name} {
        upvar $name lines
        set lines [scriptSplit [info body ${procns}::$name]]
        while {[llength $lines]} {
                set cmd [lindex $lines 0]
                uplevel 1 $cmd
                set lines [lreplace $lines[set lines {}] 0 0]
        }
}

proc stepeval {proc args} {
    set fullname [uplevel [list namespace which $proc]] 
    set procns [namespace qualifiers $fullname]
        set procname [namespace tail $proc]
    set argspec {}
    foreach arg [info args $fullname] {
        if {[info default $fullname $arg val] == 1} {
            lappend arg $val
        }
        lappend argspec $arg
    }
    tailcall apply [list $argspec [
        list [namespace current]::evaluator $procns $procname] $procns] {*}$args
}

Here is the Tcl version of the crawler tractor (see also [L1 ]):

proc tractor {} {
    eval {
        puts [incr counter]
        lappend tractor [lindex $tractor end]
        #not needed because $tractor is automatically "consumed" by the evaluator
        #if {[llength $tractor] > 3} {
        #    set tractor [lreplace $tractor[set tractor {}] 0 0]
        #}
    }
}

To execute tractor newLISP-style:

stepeval tractor

Here is a Tcl implementation of the self-modifying newLISP "factorial" function from [L2 ]:

proc factorial int {
    if {$int > 1} {
        set factorial [linsert $factorial end-1 [lindex $factorial 0]]
        set factorial [lreplace $factorial end end [list return [expr {
            $int * [lindex $factorial end end]}]]]
        incr int -1
    }
    return 1
}

stepeval factorial 5 ;# -> 120

KazimirMajorinc - 2015-12-26 03:42:53

You wrote special version of eval. There is no doubt it is possible; interpreter for every language can be written in any language. Can you do the same by only defining function crawler-tractor?

PYK 2015-12-26: There's more than that going on here. This is another example of how Tcl surfaces just the right primitives to extend it in any direction. Newlisp touts the ability to redefine the currently-running function as one of the unique features of the language. Tcl is a DSL for creating DSL's; extensibility is the name of the game. In this case, uplevel is the primitive that makes it almost trivial to extend Tcl to feature this Newlisp capability. It wasn't even necessary to drop down to the implementation level of Tcl. Time after time, the primitives Tcl provides turn out to be an eerily judicious set of tools to allow one to perform almost any computing task in almost any programming style. It's the chameleon, the Tcl Timeless Machine. It would probably be a lot of work extend Newlisp to have an uplevel capability. Maybe it would break the the way NewLisp currently works, causing one to argue that it doesn't fit the paradigm. In Tcl, it's Choose Your Own Paradigm.