[stack] Concatenative operators as functions over stacks.

"Don Groves" <[email protected]>
Newsgroups gmane.comp.lang.concatenative
Message-ID <[email protected]>
I've long been intrigued by Manfred's statement that Joy operators are
best described as functions from stacks to stacks and last night
decided to put pencil to paper and try to put that idea into some sort
of formal form -- both to aid my understanding of the implications of
this statement, and to see if something useful might emerge from the
effort. Here's a synopsis of what I've come up with so far:

A stack S is a sequence (ordered collection) of terms S = s0, s1, ...,
s(t-1), s(t) where only the most recently added term, s(t) is
available. (Note: "t" can be though of as standing for "top") Except
for the identity function, id : S --> S, all functions on S must begin
with some operation on s(t).

The function notation I use is
f : S --> S\modifications
which reads "function f is a mapping from stack S onto stack S
modified by whatever follows the backslash." So we can define push and
pop as:

push(x,S) : S --> S\t=t+1 s(t)=x
x = pop(S) : S --> S\x=s(t) t=t-1

Given any pair x,S where x is a term and S a stack, we can compute,

pop(push(x,S)) = pop(S\t=t+1 s(t)=x) = x,S\t=t+1-1 = x,S 

which is as expected.

So, here's the question -- can all required operations of a
concatenative language be defined in terms of these two primitive
stack operations and function composition?

To simplify the notation, we can get rid of the S (actually, make the
S implicit)
in the function definitions by push(x,S) = pushS(x) and pop(S) =
popS() and then
redefine them as push(x) and pop assuming there is only one stack
under  consideration.

The shuffle operators now become,

dup : S --> S\x=pop push(x) push(x)
swap : S --> S\x=pop y=pop push(x) push(y)
etc...

As Manfred points out, even concatenative literals behave as
functions. For any literal value v,
v : S --> S\push(v)

The arithmetic operators become,
add : S --> S\x=pop y=pop push(x+y)
etc...

Actually we can generalize arithmetic and relational operators as
binary operators,

binop(op) : S --> S\x=pop y=pop push(x op y) for op in
(+,-,*,/,mod,=,!=,...)

By adding additional notation for lists, sets, ..., this method seems
able to account for any concatentative operation. For example, if we
use the Haskell list notation [x|xs], then cons becomes,

cons : S --> S\x=pop [xs]=pop push([x|xs])

If we say that [Q] is a quoted program, we can define map as,

map : S --> S\[Q]=pop [y]=pop push([Q(y0) Q(y1) ... ])

The above seems to be the lambda calculus version of the situation as
it uses dummy variables. If variables are forbidden, the situation
becomes different quickly. If we try to write swap, for instance,
without dummy variables,

swap : S --> S\pop pop ? push push

we have an immediate problem -- how do we change the order of  the two
popped values? What replaces the question mark? Of course it is the C
combinator and now we're off down the combinator trail which Manfred
seems to prefer anyway.
If someone wants to take the time to produce a combinator version of
this notation, it would be a useful addition to our knowledge base, imho.

This is getting very long and I won't bore anyone with further details
but I'd like to know if anyone considers this a useful avenue for
further exploration. I'm thinking of writing a tiny interpreter using
only the methods given and hinted at here just to see how it goes and
what kind of program results.

One thing of interest is that the notation above lends itself to
direct compilation from a specification, if we remove the arrows and
just write

dup ::=  x=pop push(x) push(x)
etc...
--
Don Groves
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.