[Richard Suchenwirth] - As one more little step in [playing Prolog], here is some predicate logic in Tcl, where predicates are implemented as [proc]s created by string manipulation. The procedure with the fancy name "!" takes a Horn clause (one or more assertions) as arguments, for instance ! {human Socrates} ! {mortal $x} {human $x} With one assertion, it is taken for granted (read, in the example, "it is true that the predicate 'human' holds for Socrates", or, "Socrates is a human"). With more than one, the first assertion is true if all other assertions are true, with variables (prefixed with a dollar sign) acting as expected ("something is mortal if it is human"). Only variables can be used in this case. Each first assertion creates or extends a proc of same name (which returns truth values 1 or 0), so you can try mortal Socrates ;# => 1 The generated procedures look like this: proc human {0} {expr {$0 == "Socrates"}} proc mortal {x} {expr {[human $x]}} (remember that digits are valid variable names in Tcl - for one-arg assertions, the arguments are just numbered from 0 up with the index generator ''iota''). After another call like ! {human Plato} proc ''human'' is rewritten to proc human {0} {expr {$0 == "Socrates" || $0 == "Plato"}} Similarly, ''! {mortal $x} {animal $x}'' extends ''mortal'' to proc mortal {x} {expr {[human $x] || [animal $x]}} and so on. This is still far from Prolog: backtracking is missing, as well as semi-free variables, e.g. ! {famous $x} {founderOf $x $y} {bigCompany $y} where ''$y'' should be filled with a suitable value, but currently just raises errors. Also, predicates of different arity cannot yet be mixed. However, complex structures like ! {healthy $x} {early2bed $x} {early2rise $x} {man $x} ! {wealthy $x} {early2bed $x} {early2rise $x} {man $x} ! {wise $x} {early2bed $x} {early2rise $x} {man $x} ! {man Bill} ! {early2bed Bill} ! {early2rise Bill} can be used and go to prove that "early to bed and early to rise, makes a man healthy, wealthy, and wise", even if that may not be true in reality... } proc ! {args} { set head [lindex $args 0] set pred [lindex $head 0] set argl [lrange $head 1 end] if {[llength $args]==1} { set argv [iota [llength $argl]] foreach name $argv value $argl { lappend body "\$$name == \"$value\"" } if {[info proc $pred] != $pred} { set ebody [join $body " && "] } else { set ebody [lindex [info body $pred] 1] append ebody " || [join $body { && }]" } } else { set argv [string map {$ ""} $argl] set body "\[[join [lrange $args 1 end] {] && [}]\]" if {[info proc $pred] != $pred} { set ebody $body } else { set ebody [lindex [info body $pred] 1] append ebody " || $body" } } proc $pred $argv [list expr $ebody] } proc iota n { set res {} for {set i 0} {$i<$n} {incr i} {lappend res $i} set res } ---- [Arts and crafts of Tcl-Tk programming]