[stack] utility of the retain stack
John Nowak <[email protected]>
| Newsgroups | gmane.comp.lang.concatenative |
|---|---|
| Message-ID | <[email protected]> |
Hello all. One of the problems I've run into with a second-order
language is that you don't get partial application. (I've recently
been able to extend the intersection-based type system to a higher
order language, so this second-order restriction may not last anyway.)
Essentially, the problem is something like this. Let's say I want to
write a function that has this effect:
('a0 .. 'aN) 'x -> ('a0 F 'x G .. 'aN F 'x G) 'x
In other words, given a list of objects 'a0 to 'aN and some object 'x,
we want to map over the list applying some function F to each element,
followed by applying G with 'x on the stack. We want to keep 'x around
at the end. An implementation in Cat would look as such:
pick [[quote [dip] compose] dip compose papply map] dip
We can demonstrate its correctness as follows:
('a0 .. 'aN) 'x [F] [G] pick [[quote [dip] compose] dip compose papply
map] dip
('a0 .. 'aN) 'x [F] [G] 'x [[quote [dip] compose] dip compose papply
map] dip
('a0 .. 'aN) 'x [F] [G] [quote [dip] compose] dip compose papply map 'x
('a0 .. 'aN) 'x [F] quote [dip] compose [G] compose papply map 'x
('a0 .. 'aN) 'x [[F]] [dip] compose [G] compose papply map 'x
('a0 .. 'aN) 'x [[F] dip] [G] compose papply map 'x
('a0 .. 'aN) 'x [[F] dip G] papply map 'x
('a0 .. 'aN) ['x [F] dip G] map 'x
('a0 F 'x G .. 'aN F 'x G) 'x
This works fine (there may be an nicer way to write it), but there are
two problems. One, it's too complicated. The same thing with named
variables (i.e. naming 'x and lifting it into the function) is *much*
more direct (where \ is a lambda of sorts):
('a0 .. 'aN) 'x \y. [F y G] map
('a0 .. 'aN) [F 'x G] map
('a0 F 'x G .. 'aN F 'x G) 'x
Two, there's no way to do it in a second order language as we're
relying on higher order functions.
The solution seems to be to introduce a retain stack a la Forth (and
Factor). Here, we indicate the value of the retain stack by placing it
to the right of the dot. '+r' copies the top element of the retain
stack to the data stack:
('a0 .. 'aN) 'x >r map[F +r G] r>
('a0 .. 'aN) map[F +r G] r> . 'x
('a0 F +r G .. 'aN F +r G) r> . 'x
('a0 F 'x G .. 'aN F 'x G) r> . 'x
('a0 F 'x G .. 'aN F 'x G) 'x
It's worth nothing here that we can only evaluate as such because
we're assuming F, G, and map do not alter the retain stack. This is
something a compiler could easily enforce by tracking the retain stack
on the type level and disallowing named functions or arguments to a
combining form from using elements already on the retain stack at the
time they're called. An annotation could be used to bypass this
restriction.
Considering how much simpler this is than the version without the
retain stack, I'm wondering if Joy and Cat are missing something by
not offering that second stack. Here's how the retain stack version
might work in Joy:
('a0 .. 'aN) 'x r> [F +r G] map r>
('a0 .. 'aN) [F +r G] map r> . 'x
('a0 F +r G .. 'aN F +r G) r> . 'x
('a0 F 'x G .. 'aN F 'x G) r> . 'x
('a0 F 'x G .. 'aN F 'x G) 'x
Any thoughts or am I just rambling?
- John