cK, XY, and X provide for variable-free programming in a language
with K semantics. each language is implemented as a pure interpreter
in K.
cK implements combinators as recursive calls to eval. this approach
is by far the easiest to implement, and gives K-level performance.
XY implements combinators as prepends to the input queue. for
example, the 'i' combinator takes a quotation (list) and prepends
it to the queue. so, for example,
2 3 [4 +] i
prepends [4 +] to the queue. on subsequent calls to 'eval', 4 is
pushed, then + is evaluated.
this approach has a relatively straightforward implementation, but
is *very* slow. ("how slow is it?" [insert favorite slow joke])
to understand the approach i've taken in X, you need to know four
facts about K:
1. a function such as {[a;b;c]a+b*c} takes three arguments
a, b, c. it multiplies b and c, and then adds the result
to a. the *valence* of a function f is the number of
arguments f takes.
2. if f has valence n>1, then f[x] is a function with valence
n-1. f is projected, or "curried" on x. in particular,
f[x;y;z] = f[x][y][z].
3. there is a small, fixed set of functions, called "adverbs."
an adverb takes a function of valence n and returns a function
of valence n. different adverbs transform their functional
arguments in different ways. For example, ' (each) takes a
function f of valence n and returns a function which applies
f to corresponding elements of n equal-length lists. for
example,
{x+y*z}'[1 2 3;4 5 6;7 8 9]
applies {x+y*z} to 1 4 7, then to 2 5 8, then to 3 6 9. the
result is a list of three results.
4. all K functions return exactly one result, although that
result may be a list of arbitrary complexity.
my approach to both K adverbs and combinators in X is to implement
them as functions of quotations. An adverb converts its quotation
argument to a K function, projects it over successive items on the
stack, and, eventually, pushes its result onto the stack. A combinator
converts its quotation argument into a *list* of functions, projects
that list over successive items on the stack, and, eventually,
pushes *multiple results* onto the stack.
like XY, X is "stackless" (it doesn't use the C stack, or call
'eval' recursively), but unlike XY, it doesn't explicitly access
the input queue. like cK, X has K-level performance.
X contains all the K primitives, including the six adverbs for
fast vector manipulation. X has three fundamental combinators,
namely 'i' (spelled "i:" in X), 'dip', and 'i.', which is similar
to Joy's infra.
as time permits, i'll complete the language summary (currently a
shambles), write a short reference manual, and work up a few examples
to stress test performance.
meanwhile, the code may be viewed (and even run!) at:
http://www.nsl.com/k/x/x.k
a few X definitions are here:
http://www.nsl.com/k/x/x.x
in all, the exercise has been instructive, and immensely enjoyable.
members of the concatenative chatroom have been very helpful
(especially dan) in the course of this little project.