Richard Suchenwirth 2002-04-24 -
Joy is a programming language described by Manfred von Thun in
http://www.latrobe.edu.au/humanities/research/research-projects/past-projects/joy-programming-language, which has similarities with Forth (see
RPN in Tcl), but allows nested lists (and hence things of any complexity) as stack elements. It is radically functional (see
Steps towards functional programming) in that it is based on
functional composition and has no variables at all - no constants either, for seeming constants are considered functions that take a stack as argument, push their value on the stack, and return the augmented stack. The power of the language is rather in
combinators which take one or more functions and return a function. Here the famous K combinator can also be seen in a framework. The Joy mantra could be "Everything is a function".
To read about Joy is a mind-boggling experience. Of course I asked myself: "Can we have that in Tcl too?" How easily RPN can be implemented in Tcl was independently shown by
JCW in
Trying Forth in Tcl, so my interest focused first on certain combinators.
Primitive recursion is handled by
primrec: given two "programs", it pops the stack, and pushes the evaluation of the first "program" if the popped value is zero; otherwise it applies the second "program" to the popped value and a recursive invocation, in which the stack is decremented by 1. My simple implementation limits the first "program" to a constant and the second to a binary
expr operator, but that's enough to try the first exercises. To facilitate currying and nesting, the stack should always be the last argument to a "TclJoy" proc, and the modified stack should always be returned. Of course Tcl is Polish in that the command name is always the first word (+ 1 2), while Joy is Reversed Polish (1 2 +) - transpose it in your mind when reading such code.
By isolating the process of recursion into this combinator, typical applications of recursion can be had by simply
currying it (see
Custom curry) into an
interp alias:
interp alias {} factorial {} primrec 1 *
interp alias {} sumTo {} primrec 0 +
which approaches the original
Joy style:
DEFINE factorial == [1] [*] primrec .
Brackets are used in Joy to group lists, similar to braces in Tcl, but a closer approximation is hindered by the fact that the Tcl parser removes braces after heeding them, so {1} is indistinguishable to the called procedure from 1. Will have to think..
proc primrec {b0 bn Stack} {
set x [lindex $Stack end]
if {$x>0} {
set res [expr $x $bn [primrec $b0 $bn [incr x -1]]]
} else {
set res $b0
}
lreplace $Stack end end $res
}
# Here starts my collection of "TclJoy" components:
proc dup Stack {lappend Stack [lindex $Stack end]}
proc op2 {op Stack} {
# binary arithmetic (and comparison) engine
set x [lindex $Stack end-1]
set y [lindex $Stack end]
lreplace $Stack end-1 end [expr $x $op $y]
}
foreach op {+ - * / % > >= == <= < !=} {
interp alias {} $op {} op2 $op
}
Usage examples (note that the result is always the full stack):
primrec 1 * {foo bar 5} ==> foo bar 120
+ [dup {2 3 4}] ==> 2 3 8
Altogether, staying with Polish notation appears just too clumsy - for nested functions, you have to read from the inside out (right to left):
proc first Stack {puts Hi; set Stack}
proc then Stack {puts and; set Stack}
proc finally Stack {puts bye!; set Stack}
...
finally [then [first {}]
what in Joy, and
RPN again which has a number of operators borrowed from Joy, would look
first then finally
which admittedly reads much better.
See
Pocket Joy 2005 for
RS's latest take at Joy.