[stack] adding construction to Joy-like languages
John Nowak <[email protected]>
| Newsgroups | gmane.comp.lang.concatenative |
|---|---|
| Message-ID | <[email protected]> |
This may be more of a post-holiday brain dump than anything
interesting. If so, just ignore.
One of the things I really like about FP is the functional form of
construction. I've mentioned this in earlier emails, but here's a
quick summary:
Here's a construction involving three functions:
[f, g, h]
Applying (denoted ':') the construction to a value works as such:
[f, g, h]:x == <f:x, g:x, h:x> -- angle brackets denote a list
In short, construction is very similar to the cleave combinators in
Factor. Unfortunately, the cleave combinators are not nearly as
convenient as construction for a number of reasons:
1. The single form of construction handles any number of inputs and
outputs properly. In contrast, a family of cleave combinators is
needed. Factor has "smart combinators" that rely on the types of the
functions involved to get around this, but they're quite awkward in
comparison to FP's approach.
2. FP has functions which, given a list, return a single element from
that list. Factor has no way (that I know of) to index into a stack
returning only the element selected.
3. FP uses lists to group more than one element. As such, you can do
something like '[sq, dup]:5' to get '<25, <5, 5>>'. Emulating this
behavior is nearly impossible in Factor because all values are carried
inside a stack. As such, you must either return a flat stack like '25
5 5' (which is what '[sq] [dup] bi' would do) or return all stacks
independently like '{25} {5 5}' which makes getting at the values
painful. I've mentioned this problem earlier but I'm not sure if I'm
explaining it adequately.
The solution I propose has four parts:
1. Replace the stack with a vector in the style of More's array
theory. Here, a vector containing a single element is equivalent to
the element it contains (and vice versa). We'll denote such nestable
vectors with parentheses, e.g. '1 (2 3)' is a two element vector
containing '1' and the two element vector '(2 3)'. Note that, as in
More's array theory, '(1)' is equivalent to '1'. As such, '1 2 3 + *'
can be written '(1 (2 3 +) *)' without changing the meaning of the
expression. '()' denotes the empty vector.
2. Add functions for indexing into a vector, e.g. '1 2 3 4 2nd' yields
'3'.
3. Add a set of functions for grouping values, e.g. '1 2 3 4 2g'
yields '1 2 (3 4)'.
4. Add a syntax for construction of the form [|f0, f1 .. fN|].
What construction will do for us is take the top value of the vector
passed to it and apply every function in the construction of N
functions to it yielding a vector that is the concatenation of the
vector below the initial value and the N values produced from the
application. Whew.
Here are some examples:
1 2 3 [|sq, neg|] == 1 2 9 -3 -- works as '3' == '(3)'
3 4 2g [|+, -|] == 7 -1
5 [|sq, dup, drop|] == 25 (5 5) ()
5 12 [|1st sq, 2nd sq|] + == (5 sq) (12 sq) + == 25 144 + ==
169
Something marginally more useful:
discriminant = 3g [|2nd sq, [|1st, 3rd|] * 4 *|] -
Instead of adding a new syntax, we could alternatively offer c2, c3,
etc, to denote construction over a set number of functions:
discriminant = 3g [2nd sq] [[1st] [3rd] 2c * 4 *] 2c -
Useful? I'm not sure. The need to group things into a single vector
all the time means it is still less convenient than the FP version:
discriminant = - [sq 2nd, * [* [1st, 3rd], 4]]
This is especially if we use an infix syntax (e.g. 'f g h == g:[f,
h]') like J's monadic hook:
discriminant = (sq 2nd) - 4 * 1st * 3rd -- all associate right
- John