progress in F

stevan apter — 2006-06-22 21:12:17

"controls"

[pair![dupd!]$'!$swap!false!pair!@!] cond "[c][t][f]cond!"
[[]cond!] if "[c][t]if!"
[unit!cons!dup![;;%]$[while!],,if!] while "[c][t]while!"
[unit![pred!]swap!,'$,[]swap!while!%] do "n[f]do!"

"adverbs"

[':$[dup!count!]$swap!
[';$[consd!]$dup!swapd![[,!]$]$]do!%%] over "s[..][f]over!"
[swap!dup!proto!swap!unit!rot!over!] reduce "[..][f]reduce!"
['+reduce!] sum "[..]sum!"

"stack"

[[]swap![cons!]do!dupd!swap!'!$] get "i get!"
[[]swap!swapd![[cons!]$]do!['%$]$swap!!]set "x i set!"


get and set are useful:

10 20 30 40 2 get!
10 20 30 40 20

gets the nth item down on the stack, makes a copy, puts it at
the top.

10 20 30 40 50 2 set!
10 50 30 40

takes the item just below the index and overwrites the nth item
down on the stack.

map is giving me a headache.

sa@dfa.com — 2006-06-23 14:05:55

map in F:

[[dup!count!]$[pair![]swap!!]$swap!
[';$dup![swap!'!$]$pair![swons!]$!]
do!%%\] map! "[..][f]map!"

for example:

[[1 2 3][4 5 6 7][8 9 10 11 12]][count!]map!
[3 4 5]


[[1 2 3][4 5 6][7 8 9]][!+*]map!
[5 44 119]

joy equivalents:

$ is dup, ; is uncons, % is pop, \ is reverse, ! is i, and 'x is [x].

x y pair -> [x y]

http://www.nsl.com/k/f/prelude.f

concatenative@yahoogroups.com wrote on 06/22/2006 05:12:17 PM:

>
> "controls"
>
> [pair![dupd!]$'!$swap!false!pair!@!] cond "[c][t][f]cond!"
> [[]cond!] if "[c][t]if!"
> [unit!cons!dup![;;%]$[while!],,if!] while "[c][t]while!"
> [unit![pred!]swap!,'$,[]swap!while!%] do "n[f]do!"
>
> "adverbs"
>
> [':$[dup!count!]$swap!
> [';$[consd!]$dup!swapd![[,!]$]$]do!%%] over "s[..][f]over!"
> [swap!dup!proto!swap!unit!rot!over!] reduce "[..][f]reduce!"
> ['+reduce!] sum "[..]sum!"
>
> "stack"
>
> [[]swap![cons!]do!dupd!swap!'!$] get "i get!"
> [[]swap!swapd![[cons!]$]do!['%$]$swap!!]set "x i set!"
>
>
> get and set are useful:
>
> 10 20 30 40 2 get!
> 10 20 30 40 20
>
> gets the nth item down on the stack, makes a copy, puts it at
> the top.
>
> 10 20 30 40 50 2 set!
> 10 50 30 40
>
> takes the item just below the index and overwrites the nth item
> down on the stack.
>
> map is giving me a headache.
>
>
>
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>

William Tanksley, Jr — 2006-06-23 17:57:17

sa@... <sa@...> wrote:
> map in F:
> [[dup!count!]$[pair![]swap!!]$swap!
> [';$dup![swap!'!$]$pair![swons!]$!]
> do!%%\] map! "[..][f]map!"

F is a facinating language. I've spent a while trying to read some of
your code; not because it's especially hard to read, but because the
math is interesting.

I wonder... Could you discuss some of the design choices? For example,
you decided that "!" would be required to execute every word, thereby
indicating that the most useful thing a word can do is to leave its
address on the stack. Most concatenative languages assume that the
most common thing is to execute a word, not leave an address. I think
I can see your reasoning here -- you're not concerned about terseness
or usability, but rather about mathematical purity. I'd like to hear
your take on this.

-Billy

sa@dfa.com — 2006-06-23 18:26:24

hi billy - nice to see i've snagged your attention (awoken you
from your concatenative slumber?)

actually, a word leaves not its address on the stack, but its
value:

[1 2 3] v "assign [1 2 3] to v"
v "use v"
[1 2 3] "leaves its value on the stack"
! "unquote"
1 2 3

as a matter of fact, i find F easier to use than its cousins.
it's denser (less whitespace), but nearly as terse, since e.g.

swap!dup!proto!swap!unit!rot!over!

which happens to be the code for "reduce" would otherwise be
written

swap dup proto swap unit rot over

it's a little worse in False, where a symbol leaves itself
on the stack, and then you use ; to retrieve its value, and
! to execute:

swap;!dup;!proto;!swap;!unit;!rot;!over;!

which is more like what you describe. but i don't see any
value in breaking things down to that extent.

design choices? i don't know that i had any to begin with,
apart from what i indicated earlier. i begain with False,
added a few operations for manipulating lists, then added
the K operations. in the course of things, i kept simplifying.
at this point, it feels about right, but the real test is what
non-trivial programming is like. up til now, it's taken
me about 10-15 minutes to implement each combinator, which
isn't too bad.

concatenative@yahoogroups.com wrote on 06/23/2006 01:57:17 PM:

> sa@... <sa@...> wrote:
> > map in F:
> > [[dup!count!]$[pair![]swap!!]$swap!
> > [';$dup![swap!'!$]$pair![swons!]$!]
> > do!%%\] map! "[..][f]map!"
>
> F is a facinating language. I've spent a while trying to read some of
> your code; not because it's especially hard to read, but because the
> math is interesting.
>
> I wonder... Could you discuss some of the design choices? For example,
> you decided that "!" would be required to execute every word, thereby
> indicating that the most useful thing a word can do is to leave its
> address on the stack. Most concatenative languages assume that the
> most common thing is to execute a word, not leave an address. I think
> I can see your reasoning here -- you're not concerned about terseness
> or usability, but rather about mathematical purity. I'd like to hear
> your take on this.
>
> -Billy
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>

William Tanksley, Jr — 2006-06-23 19:54:37

sa@... <sa@...> wrote:
> hi billy - nice to see i've snagged your attention (awoken you
> from your concatenative slumber?)

Indeed :-).

> actually, a word leaves not its address on the stack, but its
> value:

Right; my typo.

I can see that one (of many) nice things about that is that there are
no "special" words. Classic Forth has a few different type of special
words; you don't need any of them.

> as a matter of fact, i find F easier to use than its cousins.
> it's denser (less whitespace), but nearly as terse, since e.g.
> swap!dup!proto!swap!unit!rot!over!

> which happens to be the code for "reduce" would otherwise be
> written
> swap dup proto swap unit rot over

It reminds me of colorforth, actually. In colorforth each word is
preceded by a token that indicates how the parser is to read the next
word; F is a bit simpler in that it doesn't have the token syntax, but
instead defines semantics for the word !.

A side topic: colorForth has the advantage that things other than
ASCII words can be embedded in the source code -- for example, in the
normal distribution, variables are embedded, so that when you edit the
source for something you're currently running you see the current
values of the variables. Another example is icons, or tables.

Of course, nothing prevents you from making a 'smart' editor for F
that could do all of that.

> it's a little worse in False, where a symbol leaves itself
> on the stack, and then you use ; to retrieve its value, and
> ! to execute:
> swap;!dup;!proto;!swap;!unit;!rot;!over;!
> which is more like what you describe. but i don't see any
> value in breaking things down to that extent.

I agree.

> design choices? i don't know that i had any to begin with,

:-)

> apart from what i indicated earlier. i begain with False,
> added a few operations for manipulating lists, then added
> the K operations. in the course of things, i kept simplifying.

It looks like a fun language.

Question: what language is it implemented in?

-Billy

stevan apter — 2006-06-23 22:02:53

>
> It looks like a fun language.

thanks - yeah, i think so.

>
> Question: what language is it implemented in?

in K (k2). i think the only primitive which would be difficult
to implement in another language (e.g. scheme or c) is @, which is K's
cross-product indexing. difficult, but not impossible. if anyone
here has the ambition to produce a c implementation, i'd be happy
to assist (read: kibbitz.)

btw, my resolve to avoid i/o in F has broken down. i've added

1 2 3 "s" 4 5 6

stops to let you examine the stack and the queue.

1 2 3 "w" 4 5 6

takes the top of the stack, formats it, writes it to stdout, then
continues on.

1 2 3 "r" 4 5 6

stops, reads from stdin, parses it, prepends it to the queue, then
continues on.

non-singleton strings are comments.

i'm satisfied that the notation makes it clear that i/o is a "sub-
language" of F.

this is all just to make debugging easier, although i can imagine
adding further i/o operators.

>
> -Billy
>

Mackenzie Straight — 2006-06-23 23:47:53

On 6/23/06, stevan apter <sa@...> wrote:
> >
> > It looks like a fun language.
>
> thanks - yeah, i think so.
>
> >
> > Question: what language is it implemented in?
>
> in K (k2). i think the only primitive which would be difficult
> to implement in another language (e.g. scheme or c) is @, which is K's
> cross-product indexing. difficult, but not impossible. if anyone
> here has the ambition to produce a c implementation, i'd be happy
> to assist (read: kibbitz.)

There are C implementations of many K verbs (to varying degrees of
correctness, but I believe @ is fine) in
http://codealchemy.org/~eiz/darcs/ok/ok_runtime.c if anyone is
interested. In particular, ok_slice and ok_slice_item implement K's
indexing. It's too bad I don't really have time to work on it anymore.

stevan apter — 2006-06-24 00:13:38

and a guest appearance by the one and only eiz. say a
few words into the microphone eiz.

----- Original Message -----
From: "Mackenzie Straight" <eizneckam@...>
To: <concatenative@yahoogroups.com>
Sent: Friday, June 23, 2006 7:47 PM
Subject: Re: [stack] progress in F


> On 6/23/06, stevan apter <sa@...> wrote:
> > >
> > > It looks like a fun language.
> >
> > thanks - yeah, i think so.
> >
> > >
> > > Question: what language is it implemented in?
> >
> > in K (k2). i think the only primitive which would be difficult
> > to implement in another language (e.g. scheme or c) is @, which is K's
> > cross-product indexing. difficult, but not impossible. if anyone
> > here has the ambition to produce a c implementation, i'd be happy
> > to assist (read: kibbitz.)
>
> There are C implementations of many K verbs (to varying degrees of
> correctness, but I believe @ is fine) in
> http://codealchemy.org/~eiz/darcs/ok/ok_runtime.c if anyone is
> interested. In particular, ok_slice and ok_slice_item implement K's
> indexing. It's too bad I don't really have time to work on it anymore.
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>

stevan apter — 2006-06-25 17:55:26

>
> I wonder... Could you discuss some of the design choices? For example,
> you decided that "!" would be required to execute every word, thereby
> indicating that the most useful thing a word can do is to leave its
> address on the stack. Most concatenative languages assume that the
> most common thing is to execute a word, not leave an address. I think
> I can see your reasoning here -- you're not concerned about terseness
> or usability, but rather about mathematical purity. I'd like to hear
> your take on this.
>
> -Billy


here's a concrete example where the F approach comes in handy.

i was working on the 'converge' combinator this morning. 'converge'
expects a quotation q on top of the stack, and beneath that, an initial
value s. it repeatedly applies q to s until either the result matches
s or is the same twice in a row. e.g.

0 [1+5 min!]converge!
5

i.e. add 1 up to 5. the combinator sets up an initial state

[0 0]

consisting of the current result and the initial value. when
the quotation is executed, the current result is updated:

[1 0]
[2 0]
[3 0]
[4 0]
[5 0]

i wanted to write 'converge' as a recursion:

[initial-state-setup
[test]
[true]
[false converge!]
cond!] converge

the problem is when the recursion happens, the state already
exists. so i rewrote this as:

[[initial-state-setup]!
[test]
[true]
[false 2 converge drop!!]
cond!] converge

when the recursion happens, "converge" causes the program
to be placed on the stack, the first two elements of which:

[[initial-state-setup] ! ....]

are dropped, and the result

[ .... ]

is executed.

whether in general it's a good idea to write "self-modifying" code,
this application seems fine to me, and quite general. we (or at
least i) often write recursions in two parts: the entry point
sets up the initial state, and then the recursion is handled by
a second entry point.

a second thought occurred to me this morning. the way F works,
when a name is encountered, its value is retrieved and placed on
the result stack. but it would be (literally) a one character
change to the implementation to have it work like joy: instead
of placing the value on the result stack, it could be prepended
to the queue, which is exactly what ! does when it encounters a
quotation. this is the way XY works, and there is a reason i've
never liked it.

if you define

[2+] plus2

then

3 plus2
5

but suppose you want to define something you'll use as a *data
list*, e.g.

[1 2 3] v123

now, if you say

v123
1 2 3

you get three items on the stack. so you have to say

[[1 2 3]] v123

to get the *list* of 1 2 3.

obviously, joy doesn't have this problem, since it doesn't contain
assignment. in joy's definition metalanguage, you don't have to
quote the body of the program:

plus2 == 2 +
v123 == [1 2 3]

but it seems to me that any concatenative language which wants to
support assignment will have to deal with this problem (possibly
by having two types of assignment.)

sa@dfa.com — 2006-06-27 15:20:00

how would you write joy's 'stack' and 'unstack' functions in terms of
the remaining primitives?

in trying to solve this problem for F, i was led to alter behavior
which i think is pretty standard for stack languages, viz., what
happens when a function expecting n elements is applied to a stack
containing fewer than n elements.

instead of a "short stack" error, i have the function in question
behave like the identity function:

2 +
2

so now suppose that the stack is

10 20 30 40

so

[] cons!
10 20 30 [40]
cons!
10 20 [30 40]
cons!
10 [20 30 40]
cons!
[10 20 30 40]
cons!
[10 20 30 40]

the 'converge' combinator applies the quotation at the top of
the stack until the result is the same twice in a row or the
initial state reoccurs. so,

10 20 30 40 [] [cons!] converge!
[10 20 30 40]

the 'unstack' function also uses 'converge': it expects a
quotation at the top of the stack which is made the new
stack.

10 20 30 40 [50 60] unstack!
50 60

'unstack' applies 'stack' to the stack beneath the quotation,
pops it off, converges the 'uncons' program on the quotation,
then pops off the two elements on top:

10 20 30 40 [50 60][stack!pop!]dip![uncons!]converge!pop!pop!
50 60

n.b.: [] uncons -> null []. [] matches the initial state, so
'converge' terminates, and then we pop null and [].

William Tanksley, Jr — 2006-06-28 11:53:44

sa@... <sa@...> wrote:
> how would you write joy's 'stack' and 'unstack' functions in terms of
> the remaining primitives?
[...]
> instead of a "short stack" error, i have the function in question
> behave like the identity function:

That's a great solution to implementing stack and unstack. Any
function that depends on the depth of the stack for its operation,
however, is by definition badly factored; therefore although I
recognise the need/desire to imitate Joy, I don't like those two
words, and I seriously don't like any systemic behavior that depends
on the depth of the stack.

Even though it requires you to add another primitive, I would rather
define a DEPTH word that simply returned the current depth of the
stack, so that the evil could be contained entirely within one word
rather than spread throughout the system.

-Billy (eeeeevil)

sa@dfa.com — 2006-06-28 14:31:19

concatenative@yahoogroups.com wrote on 06/28/2006 07:53:44 AM:

> sa@... <sa@...> wrote:
> > how would you write joy's 'stack' and 'unstack' functions in terms of
> > the remaining primitives?
> [...]
> > instead of a "short stack" error, i have the function in question
> > behave like the identity function:
>
> That's a great solution to implementing stack and unstack. Any
> function that depends on the depth of the stack for its operation,
> however, is by definition badly factored; therefore although I
> recognise the need/desire to imitate Joy, I don't like those two
> words, and I seriously don't like any systemic behavior that depends
> on the depth of the stack.

these words are in a class by themselves, and i'm not surprised
that you dislike them. my main reason for seeking an implementation
was not to imitate joy directly, but rather to give myself (or
rather F) the resources to define joy's recursive combinators.
wherever a combinator must save the stack, do something, and
then restore the stack, the "something" has to be framed by
operations involving 'stack' and 'unstack'. at least, so it
seems to me.

>
> Even though it requires you to add another primitive, I would rather
> define a DEPTH word that simply returned the current depth of the
> stack, so that the evil could be contained entirely within one word
> rather than spread throughout the system.

we're talking about two kinds of evil: a primtive (DEPTH) which
contaminates every definition which uses it, and the handling of
an edge-condition in the processing of the stack. i'm not
convinced that the latter actually is evil. i'd like to see
a principled argument for that conclusion. (there's already
a practical one: it makes debugging more arduous.)

>
> -Billy (eeeeevil)
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>

William Tanksley, Jr — 2006-06-29 00:30:44

sa@... <sa@...> wrote:
> > That's a great solution to implementing stack and unstack. Any
> > function that depends on the depth of the stack for its operation,
> > however, is by definition badly factored; therefore although I
> > recognise the need/desire to imitate Joy, I don't like those two
> > words, and I seriously don't like any systemic behavior that depends
> > on the depth of the stack.

> these words are in a class by themselves, and i'm not surprised
> that you dislike them. my main reason for seeking an implementation
> was not to imitate joy directly, but rather to give myself (or
> rather F) the resources to define joy's recursive combinators.
> wherever a combinator must save the stack, do something, and
> then restore the stack, the "something" has to be framed by
> operations involving 'stack' and 'unstack'. at least, so it
> seems to me.

No, you're totally right. This is why I also don't like Joy's habit of
saving and restoring the entire stack. I would rather define things
such that no full-stack saving and restoring is needed; most other
pre-Joy concatenative languages work that way, so there's no doubt it
can be done. (Of course, you're proving that you can do the same
things as Joy, so I accept that you must perform full-stack saving and
restoring.)

> > Even though it requires you to add another primitive, I would rather
> > define a DEPTH word that simply returned the current depth of the
> > stack, so that the evil could be contained entirely within one word
> > rather than spread throughout the system.

> we're talking about two kinds of evil: a primtive (DEPTH) which
> contaminates every definition which uses it, and the handling of
> an edge-condition in the processing of the stack. i'm not
> convinced that the latter actually is evil. i'd like to see
> a principled argument for that conclusion. (there's already
> a practical one: it makes debugging more arduous.)

The primitive named 'DEPTH' can be searched for using any text
utility. The edge condition cannot be determined to be absent except
by whole-program semantic analysis (and sometimes that isn't
possible); you can't even show that it's present without luck that
enables you to execute exactly the right conditions to run across it
AND notice the effects. Which one would you like to think about when
developing a 1 million lines of code system?

I would rather have the edge condition return an immediate error. The
sooner the better.

And I'm not even talking about static typechecking here. I will say
that if you've got static typechecking this isn't a problem -- you can
make that edge condition behave anyhow you'd like, because the
programmer can control within the word whether the edge condition will
apply.

-Billy

stevan apter — 2006-06-29 11:20:34

----- Original Message -----
From: "William Tanksley, Jr" <wtanksleyjr@...>
To: <concatenative@yahoogroups.com>
Sent: Wednesday, June 28, 2006 8:30 PM
Subject: Re: [stack] Re: 'stack' and 'unstack' in F


> sa@... <sa@...> wrote:
> > > That's a great solution to implementing stack and unstack. Any
> > > function that depends on the depth of the stack for its operation,
> > > however, is by definition badly factored; therefore although I
> > > recognise the need/desire to imitate Joy, I don't like those two
> > > words, and I seriously don't like any systemic behavior that depends
> > > on the depth of the stack.
>
> > these words are in a class by themselves, and i'm not surprised
> > that you dislike them. my main reason for seeking an implementation
> > was not to imitate joy directly, but rather to give myself (or
> > rather F) the resources to define joy's recursive combinators.
> > wherever a combinator must save the stack, do something, and
> > then restore the stack, the "something" has to be framed by
> > operations involving 'stack' and 'unstack'. at least, so it
> > seems to me.
>
> No, you're totally right. This is why I also don't like Joy's habit of
> saving and restoring the entire stack. I would rather define things
> such that no full-stack saving and restoring is needed; most other
> pre-Joy concatenative languages work that way, so there's no doubt it
> can be done. (Of course, you're proving that you can do the same
> things as Joy, so I accept that you must perform full-stack saving and
> restoring.)

forth and factor, to take two examples, have >r and r> (have i got
their names right?), and possibly something like DEPTH as well, isn't
that so? i think if you have those at your disposal, you have enough
to define e.g. 'linrec'.

i think we've discussed this in the past: in joy, you can have
something like

X [1 2 3][+]map

which evaluates

X 1 +
X 2 +
X 3 +

and leaves

X [x y z]

on the stack. how would you implement this behavior without some
mechanism for saving and restoring the stack before and after each
application of +?

>
> > > Even though it requires you to add another primitive, I would rather
> > > define a DEPTH word that simply returned the current depth of the
> > > stack, so that the evil could be contained entirely within one word
> > > rather than spread throughout the system.
>
> > we're talking about two kinds of evil: a primtive (DEPTH) which
> > contaminates every definition which uses it, and the handling of
> > an edge-condition in the processing of the stack. i'm not
> > convinced that the latter actually is evil. i'd like to see
> > a principled argument for that conclusion. (there's already
> > a practical one: it makes debugging more arduous.)
>
> The primitive named 'DEPTH' can be searched for using any text
> utility. The edge condition cannot be determined to be absent except
> by whole-program semantic analysis (and sometimes that isn't
> possible); you can't even show that it's present without luck that
> enables you to execute exactly the right conditions to run across it
> AND notice the effects. Which one would you like to think about when
> developing a 1 million lines of code system?
>
> I would rather have the edge condition return an immediate error. The
> sooner the better.
>
> And I'm not even talking about static typechecking here. I will say
> that if you've got static typechecking this isn't a problem -- you can
> make that edge condition behave anyhow you'd like, because the
> programmer can control within the word whether the edge condition will
> apply.
>
> -Billy
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>

Christopher Diggins — 2006-06-29 16:40:28

> > That's a great solution to implementing stack and unstack. Any
> > function that depends on the depth of the stack for its operation,
> > however, is by definition badly factored; therefore although I
> > recognise the need/desire to imitate Joy, I don't like those two
> > words, and I seriously don't like any systemic behavior that depends
> > on the depth of the stack.
I have a similar issue in Cat. A function which only looks at the top two
items of a stack, behaves effectively like a pure binary function in the
average functional language. However, if we introduce any primitives in its
definition which manipulate or even observe the stack as a whole, it becomes
more akin to a procedure in an imperative language with a side-effect.

The solution I am leaning towards is to label functions with no side-effects
as being "clean", while functions which examine the entire stack are
"dirty". This is my attempt to resolve the differences between the idea of
pure functional programming and imperative programming.

Some discussion of this approach and alternatives can be found at
http://www.artima.com/forums/flat.jsp?forum=106&thread=166178&start=0&msRange=15


[Non-text portions of this message have been removed]

sa@dfa.com — 2006-06-29 17:46:24

my implementation of 'stack' doesn't "observe the stack as a whole".
there's no primitive in F which can do that. what 'stack' does
is, first, push [] onto the stack, and then repeatedly apply
'cons' (which is a primitive) until the stack is empty, at which
point the operation terminates.

nor does 'stack' depend on the depth of the stack. in fact, i
share billy's suspicion of any function which does.

slava had what i thought might be a good objection (to treating
stack underflow as application of the identity function):

consider the 'swap' primitive:

2 3 \
3 2

you can define something similar (in F):

[pair!_unpair!] swap

i.e. make a pair of the top two elements on the stack, reverse
it, put the elements back on the stack. but

2 \
2
swap!
2 0

it doesn't really matter why 'swap' does this. in general it
will be true that compound words which attempt to emulate
primitives will behave differently from those primitives
when applied to a short stack.

i think it is a good objection, but not a fatal one. it
would be fatal only if one required that compound words
behaved like primitives in all ways, which i do not.

concatenative@yahoogroups.com wrote on 06/29/2006 12:40:28 PM:

> > > That's a great solution to implementing stack and unstack. Any
> > > function that depends on the depth of the stack for its operation,
> > > however, is by definition badly factored; therefore although I
> > > recognise the need/desire to imitate Joy, I don't like those two
> > > words, and I seriously don't like any systemic behavior that depends
> > > on the depth of the stack.
> I have a similar issue in Cat. A function which only looks at the top two
> items of a stack, behaves effectively like a pure binary function in the
> average functional language. However, if we introduce any primitives in
its
> definition which manipulate or even observe the stack as a whole, it
becomes
> more akin to a procedure in an imperative language with a side-effect.
>
> The solution I am leaning towards is to label functions with no
side-effects
> as being "clean", while functions which examine the entire stack are
> "dirty". This is my attempt to resolve the differences between the idea
of
> pure functional programming and imperative programming.
>
> Some discussion of this approach and alternatives can be found at
>
http://www.artima.com/forums/flat.jsp?forum=106&thread=166178&start=0&msRange=15

>
>
> [Non-text portions of this message have been removed]
>
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>

Christopher Diggins — 2006-06-29 21:52:02

> my implementation of 'stack' doesn't "observe the stack as a whole".
> there's no primitive in F which can do that. what 'stack' does
> is, first, push [] onto the stack, and then repeatedly apply
> 'cons' (which is a primitive) until the stack is empty, at which
> point the operation terminates.

I said "... if we introduce any primitives in its definition which
manipulate or even observe the stack ...".

Obviously what you just described not only observes the entire stack, it
manipulates it!


[Non-text portions of this message have been removed]

stevan apter — 2006-06-29 22:37:58

----- Original Message -----
From: "Christopher Diggins" <cdiggins@...>
To: <concatenative@yahoogroups.com>
Sent: Thursday, June 29, 2006 5:52 PM
Subject: Re: [stack] Re: 'stack' and 'unstack' in F


> > my implementation of 'stack' doesn't "observe the stack as a whole".
> > there's no primitive in F which can do that. what 'stack' does
> > is, first, push [] onto the stack, and then repeatedly apply
> > 'cons' (which is a primitive) until the stack is empty, at which
> > point the operation terminates.
>
> I said "... if we introduce any primitives in its definition which
> manipulate or even observe the stack ...".
>
> Obviously what you just described not only observes the entire stack, it
> manipulates it!

this is (or could become) a dispute over vocabulary.

the primitive + has valence 2 and charge 1. by that i mean
that it consumes 2 elements from the stack and leaves 1. 'dup'
has valence 1 and charge 2. and so forth.

now, what is valence of 'stack', and what is the charge of
'unstack'. i think it makes sense to say that the valence
of 'stack' is infinite (or variable), and the charge of 'unstack'
is infinite (or variable). (the charge of 'stack' is 1: if
there are n elements on the input stack, there will be n+1
elements on the output stack. the valence of 'unstack' is 1.)

what i meant when i said that no F primitive "observes the
stack as a whole" i meant that no F primitive has infinite
(or variable) valence.

the 'stack' *combinator* has infinite valence, but it's
not a primitive. indeed, that was the problem: how to
define such a combinator given that all primitives have
fixed, finite valence. (and ditto for 'unstack' and
charge.)


>
>
> [Non-text portions of this message have been removed]
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>

John Cowan — 2006-06-29 22:20:44

Christopher Diggins scripsit:

> I have a similar issue in Cat. A function which only looks at the top two
> items of a stack, behaves effectively like a pure binary function in the
> average functional language. However, if we introduce any primitives in its
> definition which manipulate or even observe the stack as a whole, it becomes
> more akin to a procedure in an imperative language with a side-effect.

How so? All Joy words are functions from a stack to a stack, with the
exception of those which do file operations.

> The solution I am leaning towards is to label functions with no side-effects
> as being "clean", while functions which examine the entire stack are
> "dirty". This is my attempt to resolve the differences between the idea of
> pure functional programming and imperative programming.

What's the difference between looking at one, or two, or three, or ..., and looking
at all of them? It seems completely arbitrary to me.

--
So that's the tune they play on John Cowan
their fascist banjos, is it? cowan@...
--Great-Souled Sam http://www.ccil.org/~cowan

Martin Young — 2006-06-29 23:25:21

Hi Stevan,

On Thu, 2006-06-29 at 18:37 -0400, stevan apter wrote:
> what i meant when i said that no F primitive "observes the
> stack as a whole" i meant that no F primitive has infinite
> (or variable) valence.
>
> the 'stack' *combinator* has infinite valence, but it's
> not a primitive. indeed, that was the problem: how to
> define such a combinator given that all primitives have
> fixed, finite valence. (and ditto for 'unstack' and
> charge.)

Could you explain why it's important to you that primitives have fixed
finite valence and charge yet it's not important for combinators?

The only reason I can think of is that fixed valence and charge help
when performing automatic analysis and optimisation. However, a
combinator with variable valence or charge would be just as much an
effective block to static analysis as such a primitive.

Put pragmatically you can't statically unroll a loop with an unbound
number or iterations regardless of whether you can decompose the code
itself into fixed valence primitives.

Regards,
Martin.

Christopher Diggins — 2006-06-29 23:57:07

> What's the difference between looking at one, or two, or three, or ...,
and looking
at all of them? It seems completely arbitrary to me.

I find it useful to think about functions in a concatenative language as
both producers and consumers of elements on the top of the stack. You can
then define the type of a function as the number and type of the stack
elements that it consumes and the elements that it produces.

This makes it easier to define a mapping from an arbitrary functional or
imperative language to a concatenative language, which is why I am
developing Cat: as a backend to the Heron langauge, and potentially other
languages.

This also is useful for constructing a type-system for a concatenative
language.

Consider how the defintion of a function might be better described if we had
a type-system for describing the arguments and results of functions.


[Non-text portions of this message have been removed]

stevan apter — 2006-06-30 03:20:21

hi martin

in fact, all the primitives of F have fixed finite valence,
but the two primitive combinators ! (i) and ` (dip) have variable
charge. a program, all the atoms of which have fixed valence,
will have fixed valence. as you say, the main use for these
concepts is in the area of automatic analysis and optimization.

i invoked these concepts in order to explain my decision to
replace 'stack underflow error' with a different, non-error
behavior. i still think it's remarkable that 'stack' and
'unstack' (whatever you may think of them) depend on this
behavior, and that replicating the behavior of certain joy
combinators (whatever you think of them) appears to require
these programs.

for example, if you define 'map' so that the quotation which
is mapped to items of a list doesn't "reach beyond" those
items to the stack below, then you can define it in F this
way:

[[$count!]`[pair![]\!]`\
[':`$[\'!`]`pair![swons!]`!]
do!%%_] map

for example,

10 20 30 [[1 2 3][4 5 6][7 8 9]][!+*] map!
10 20 30 [5 44 119]

but suppose you want to have:

10 20 30 [[2 3][4 5 6][9]][!+*] Map!
10 20 30 [150 44 780]

that is, the first application of [!+*] sees 30 2 3, the
second sees 4 5 6, the third sees 20 30 9.

for this, you need 'stack':

[[[stack!]`]`
[[unit!]map!',eachright!]`
',eachleft![unit!.last!]map!] Map

i'm still not an expert F-er, so probably both programs
could be tightened up, but i think my general claim still
stands.

btw, in the joy dialect of F, 'map' is:

[[dup count] dip [pair [] swap i] dip swap [quote uncons dip dup
[swap quote i dip] dip pair quote swons dip i] do pop pop rev] map

----- Original Message -----
From: "Martin Young" <martin@...>
To: <concatenative@yahoogroups.com>
Sent: Thursday, June 29, 2006 7:25 PM
Subject: [stack] Re: 'stack' and 'unstack' in F


> Hi Stevan,
>
> On Thu, 2006-06-29 at 18:37 -0400, stevan apter wrote:
> > what i meant when i said that no F primitive "observes the
> > stack as a whole" i meant that no F primitive has infinite
> > (or variable) valence.
> >
> > the 'stack' *combinator* has infinite valence, but it's
> > not a primitive. indeed, that was the problem: how to
> > define such a combinator given that all primitives have
> > fixed, finite valence. (and ditto for 'unstack' and
> > charge.)
>
> Could you explain why it's important to you that primitives have fixed
> finite valence and charge yet it's not important for combinators?
>
> The only reason I can think of is that fixed valence and charge help
> when performing automatic analysis and optimisation. However, a
> combinator with variable valence or charge would be just as much an
> effective block to static analysis as such a primitive.
>
> Put pragmatically you can't statically unroll a loop with an unbound
> number or iterations regardless of whether you can decompose the code
> itself into fixed valence primitives.
>
> Regards,
> Martin.
>
>
>
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>

stevan apter — 2006-06-30 03:22:26

how many elements, and of what types, does cat's equivalent
of 'i' produce? in my lingo, does cat have combinators of
variable charge?

----- Original Message -----
From: "Christopher Diggins" <cdiggins@...>
To: <concatenative@yahoogroups.com>
Sent: Thursday, June 29, 2006 7:57 PM
Subject: Re: [stack] Re: 'stack' and 'unstack' in F


> > What's the difference between looking at one, or two, or three, or ...,
> and looking
> at all of them? It seems completely arbitrary to me.
>
> I find it useful to think about functions in a concatenative language as
> both producers and consumers of elements on the top of the stack. You can
> then define the type of a function as the number and type of the stack
> elements that it consumes and the elements that it produces.
>
> This makes it easier to define a mapping from an arbitrary functional or
> imperative language to a concatenative language, which is why I am
> developing Cat: as a backend to the Heron langauge, and potentially other
> languages.
>
> This also is useful for constructing a type-system for a concatenative
> language.
>
> Consider how the defintion of a function might be better described if we had
> a type-system for describing the arguments and results of functions.
>
>
> [Non-text portions of this message have been removed]
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>

William Tanksley, Jr — 2006-06-30 13:43:07

stevan apter <sa@...> wrote:
> what i meant when i said that no F primitive "observes the
> stack as a whole" i meant that no F primitive has infinite
> (or variable) valence.

If you accept your proposed rule (that operating on a stack without
enough values is a valid no-op), every primitive that takes any
arguments has variable valence. Am I missing something?

> the 'stack' *combinator* has infinite valence, but it's
> not a primitive. indeed, that was the problem: how to
> define such a combinator given that all primitives have
> fixed, finite valence. (and ditto for 'unstack' and
> charge.)

The primitives may appear to have fixed valence, but they don't -- the
recursion and conditional constructs (or loop construct) make the
valence variable. So just iterate over the stack.

-Billy

William Tanksley, Jr — 2006-06-30 13:54:16

stevan apter <sa@...> wrote:
> how many elements, and of what types, does cat's equivalent
> of 'i' produce? in my lingo, does cat have combinators of
> variable charge?

By the way, I can't answer for cat, but strongForth has NO words with
variable valence or charge. All words produce a static stack effect,
even the conditionals and EXECUTE (which is equivalent to 'i').

Actually, the word EXECUTE doesn't exist; instead it's replaced by
EXECUTE(. The open parenthesis indicates that it's a parsing word; it
consumes source code to determine the stack effect of the quotation
it's allowed to execute. Quotations are also staticly typechecked, so
it's impossible to compile a program that isn't typesafe in this
sense.

Here's a crude example using Joylike syntax together with
strongForth's type notation:

[( -- int) 3 4 +] execute( -- int)

strongForth lacks some important features which would make it more
generally useful. The most important of these is array operations on
dynamicly resizable arrays.

-Billy

William Tanksley, Jr — 2006-06-30 14:04:53

stevan apter <sa@...> wrote:
> i invoked these concepts in order to explain my decision to
> replace 'stack underflow error' with a different, non-error
> behavior. i still think it's remarkable that 'stack' and
> 'unstack' (whatever you may think of them) depend on this
> behavior, and that replicating the behavior of certain joy
> combinators (whatever you think of them) appears to require
> these programs.

This may be faulty logic. It's true that Joy combinators require those
programs, and so if you're precisely imitating Joy you need stack and
unstack; but it's not true that stack or unstack REQUIRE the behavior
we're discussing. There are many alternates -- for example, a
conditional version of ! or converge that will act the way you're
describing, or a primitive that returns the current stack depth.

The problem with your suggestion is that it's a whole-language change;
it can be used anywhere invisibly, and in fact is easy to use
accidentally. In fact, it's way easier to use accidentally than it is
to use on purpose, except for the single purpose you propose
(consuming the entire stack).

-Billy

sa@dfa.com — 2006-06-30 15:52:15

concatenative@yahoogroups.com wrote on 06/30/2006 09:43:07 AM:

> stevan apter <sa@...> wrote:
> > what i meant when i said that no F primitive "observes the
> > stack as a whole" i meant that no F primitive has infinite
> > (or variable) valence.
>
> If you accept your proposed rule (that operating on a stack without
> enough values is a valid no-op), every primitive that takes any
> arguments has variable valence. Am I missing something?

in one sense, i guess so: a primitive has valence n when there
are at least n items on the stack, else it has valence 0/charge 0.
alternatively, you could say that every primitive has valence n,
but that "+" (any primitive) is the identity function when there
are fewer than n items on the stack.

>
> > the 'stack' *combinator* has infinite valence, but it's
> > not a primitive. indeed, that was the problem: how to
> > define such a combinator given that all primitives have
> > fixed, finite valence. (and ditto for 'unstack' and
> > charge.)
>
> The primitives may appear to have fixed valence, but they don't -- the
> recursion and conditional constructs (or loop construct) make the
> valence variable. So just iterate over the stack.

? i don't understand this. there are no recursion or conditional
or looping primitives in F. can you perhaps expand on this?

>
> -Billy
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>

sa@dfa.com — 2006-06-30 16:31:54

concatenative@yahoogroups.com wrote on 06/30/2006 10:04:53 AM:

> stevan apter <sa@...> wrote:
> > i invoked these concepts in order to explain my decision to
> > replace 'stack underflow error' with a different, non-error
> > behavior. i still think it's remarkable that 'stack' and
> > 'unstack' (whatever you may think of them) depend on this
> > behavior, and that replicating the behavior of certain joy
> > combinators (whatever you think of them) appears to require
> > these programs.
>
> This may be faulty logic. It's true that Joy combinators require those
> programs, and so if you're precisely imitating Joy you need stack and
> unstack; but it's not true that stack or unstack REQUIRE the behavior
> we're discussing. There are many alternates -- for example, a
> conditional version of ! or converge that will act the way you're
> describing, or a primitive that returns the current stack depth.

well, what i actually said was that 'stack' could be defined with
my method, but absent DEPTH or some equivalent primitive of variable
valence, it couldn't.

>
> The problem with your suggestion is that it's a whole-language change;
> it can be used anywhere invisibly, and in fact is easy to use
> accidentally. In fact, it's way easier to use accidentally than it is
> to use on purpose, except for the single purpose you propose
> (consuming the entire stack).

actually, i rely on the behavior for 'unstack' as well, but
otherwise what you say is probably right. and i'm not sure
it doesn't have other applications.

>
> -Billy
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>

William Tanksley, Jr — 2006-06-30 17:49:15

sa@... <sa@...> wrote:
> > If you accept your proposed rule (that operating on a stack without
> > enough values is a valid no-op), every primitive that takes any
> > arguments has variable valence. Am I missing something?

> in one sense, i guess so: a primitive has valence n when there
> are at least n items on the stack, else it has valence 0/charge 0.
> alternatively, you could say that every primitive has valence n,
> but that "+" (any primitive) is the identity function when there
> are fewer than n items on the stack.

So what am I missing? It looks like you're agreeing with me that every
primitive in your proposed system has variable valence (and charge).
Your "alternatively" phrase is a bit confusing -- are you trying to
claim that because a function's valence is constant, a primitive that
implements two different functions therefore has a constant valence?

> > > the 'stack' *combinator* has infinite valence, but it's
> > > not a primitive. indeed, that was the problem: how to
> > > define such a combinator given that all primitives have
> > > fixed, finite valence. (and ditto for 'unstack' and
> > > charge.)

> > The primitives may appear to have fixed valence, but they don't -- the
> > recursion and conditional constructs (or loop construct) make the
> > valence variable. So just iterate over the stack.

> ? i don't understand this. there are no recursion or conditional
> or looping primitives in F. can you perhaps expand on this?

Isn't 'converge' a looping primitive? Its valence is certainly variable.

-Billy

sa@dfa.com — 2006-06-30 18:06:36

concatenative@yahoogroups.com wrote on 06/30/2006 01:49:15 PM:

> sa@... <sa@...> wrote:
> > > If you accept your proposed rule (that operating on a stack without
> > > enough values is a valid no-op), every primitive that takes any
> > > arguments has variable valence. Am I missing something?
>
> > in one sense, i guess so: a primitive has valence n when there
> > are at least n items on the stack, else it has valence 0/charge 0.
> > alternatively, you could say that every primitive has valence n,
> > but that "+" (any primitive) is the identity function when there
> > are fewer than n items on the stack.
>
> So what am I missing? It looks like you're agreeing with me that every
> primitive in your proposed system has variable valence (and charge).
> Your "alternatively" phrase is a bit confusing -- are you trying to
> claim that because a function's valence is constant, a primitive that
> implements two different functions therefore has a constant valence?

yeah, that's what i was trying to do, but i guess i didn't
get away with it. :)

seriously though, i think i can claim that F has 30 primitives,
29 of which are associated with symbols, and one of which --
the identity function -- has no symbol; and that a symbol
denotes the function with which it is associated unless the
stack is short, in which case it denotes the identity function.

>
> > > > the 'stack' *combinator* has infinite valence, but it's
> > > > not a primitive. indeed, that was the problem: how to
> > > > define such a combinator given that all primitives have
> > > > fixed, finite valence. (and ditto for 'unstack' and
> > > > charge.)
>
> > > The primitives may appear to have fixed valence, but they don't --
the
> > > recursion and conditional constructs (or loop construct) make the
> > > valence variable. So just iterate over the stack.
>
> > ? i don't understand this. there are no recursion or conditional
> > or looping primitives in F. can you perhaps expand on this?
>
> Isn't 'converge' a looping primitive? Its valence is certainly variable.

'converge' isn't a primitive. here's its definition (in the Joy dialect):

[Converge last]

and here is 'Converge':

[[dupd dupd quote pair dip swapd] i [[bot quote i dip swap dup] dip
pair [pair dup unpair swap in] dip swap] [pop first rev 1 swap drop]
[[rev unpair cons] dip unpair 2 [Converge []] dot drop i] cond]

really, there aren't any recursive or looping primitives in F. just
'i' and 'dip'.

>
> -Billy
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>

William Tanksley, Jr — 2006-06-30 18:23:48

sa@... <sa@...> wrote:
> > > i invoked these concepts in order to explain my decision to
> > > replace 'stack underflow error' with a different, non-error
> > > behavior. i still think it's remarkable that 'stack' and
> > > 'unstack' (whatever you may think of them) depend on this
> > > behavior, and that replicating the behavior of certain joy
> > > combinators (whatever you think of them) appears to require
> > > these programs.
> >
> > This may be faulty logic. It's true that Joy combinators require those
> > programs, and so if you're precisely imitating Joy you need stack and
> > unstack; but it's not true that stack or unstack REQUIRE the behavior
> > we're discussing. There are many alternates -- for example, a
> > conditional version of ! or converge that will act the way you're
> > describing, or a primitive that returns the current stack depth.

> well, what i actually said was that 'stack' could be defined with
> my method, but absent DEPTH or some equivalent primitive of variable
> valence, it couldn't.

That statement is neither useful nor true. (No offense meant, just
good clean fun.)

I'll prove by counterexample that it's untrue: I can define a language
in which 'stack' could be defined without any variable valence
primitives -- not even a variable loop. This hypothetical language has
a stack that's a constant depth (probably a circular buffer), so
saving it to an array requires a constant number of operations. This
is an extreme counterexample, and I suspect that some gentler one
probably exists -- but at the same time, note that one concatenative
language actually works this way (Chuck Moore's machine forth and
colorforth).

The reason I said that your statement wasn't useful is that you said
"stack can be defined with my method" (which is true), and then "stack
can not be defined without primitives of variable valence" (since your
solution requires all primitives to have variable valence, it also
excludes your method). So therefore even if we assume that the second
statement is true, it doesn't say anything about my solution that it
doesn't also say about yours.

I just thought of a gentler counterexample: a primitive named 'empty?'
could be used to implement 'stack'. It returns true when the stack is
empty, false otherwise. Its valence is undetectably close to zero,
because it doesn't actually consume anything; its charge is a constant
1. (It's not as good of a counterexample as my previous one, because
it may be argued that its valence is 1 when the stack's full and 0
when the stack's empty -- but that would be a pretty subtle argument.)

> > The problem with your suggestion is that it's a whole-language change;
> > it can be used anywhere invisibly, and in fact is easy to use
> > accidentally. In fact, it's way easier to use accidentally than it is
> > to use on purpose, except for the single purpose you propose
> > (consuming the entire stack).

> actually, i rely on the behavior for 'unstack' as well, but
> otherwise what you say is probably right. and i'm not sure
> it doesn't have other applications.

You're right: one possible application would be using your behavior to
define DEPTH :-). My point was just that misusing it is very easy and
invisible (you can't test a program by running it at the command line,
because it might behave differently 'in vivo'), and using it correctly
is hard for any job that doesn't naturally start with "first, consume
the entire stack". In fact, every time you deliberately use it, you
must start by first consuming the entire stack (and then possibly put
some of it back).

-Billy

William Tanksley, Jr — 2006-06-30 18:51:16

sa@... <sa@...> wrote:
> > sa@... <sa@...> wrote:
> > > > If you accept your proposed rule (that operating on a stack without
> > > > enough values is a valid no-op), every primitive that takes any
> > > > arguments has variable valence. Am I missing something?
> > > in one sense, i guess so: a primitive has valence n when there
> > > are at least n items on the stack, else it has valence 0/charge 0.
> > > alternatively, you could say that every primitive has valence n,
> > > but that "+" (any primitive) is the identity function when there
> > > are fewer than n items on the stack.

> > So what am I missing? It looks like you're agreeing with me that every
> > primitive in your proposed system has variable valence (and charge).
> > Your "alternatively" phrase is a bit confusing -- are you trying to
> > claim that because a function's valence is constant, a primitive that
> > implements two different functions therefore has a constant valence?

> yeah, that's what i was trying to do, but i guess i didn't
> get away with it. :)

Yup, you're a sneaky guy, always trying to get away with things :-).
Good thing I'm here to keep an eye on you.

> seriously though, i think i can claim that F has 30 primitives,
> 29 of which are associated with symbols, and one of which --
> the identity function -- has no symbol; and that a symbol
> denotes the function with which it is associated unless the
> stack is short, in which case it denotes the identity function.

So when you used the word 'function' earlier, you actually meant
'primitive', and when you used 'primitive' you meant 'predefined
symbol'. I don't have any problem with that claim, but it doesn't seem
to add anything to the discussion -- it seems like a mere search and
replace, with no change to the logic or conclusions.

In either case, the programs users type in will have to deal with
variable valence whether they want to or not. A language using DEPTH
will only have to deal with it only when it actually appears in the
text.

> > > > > the 'stack' *combinator* has infinite valence, but it's
> > > > > not a primitive. indeed, that was the problem: how to
> > > > > define such a combinator given that all primitives have
> > > > > fixed, finite valence. (and ditto for 'unstack' and
> > > > > charge.)

> > > > The primitives may appear to have fixed valence, but they don't --
> > > > the recursion and conditional constructs (or loop construct) make
> > > > the valence variable. So just iterate over the stack.

> > > ? i don't understand this. there are no recursion or conditional
> > > or looping primitives in F. can you perhaps expand on this?

> > Isn't 'converge' a looping primitive? Its valence is certainly variable.

> 'converge' isn't a primitive. here's its definition (in the Joy dialect):

My mistake. Okay, then "!" makes the valence as variable as you want
it to be, because its valence is variable and includes the possiblity
of executing itself. Same thing, really.

> really, there aren't any recursive or looping primitives in F. just
> 'i' and 'dip'.

Nice :-). I like it.

-Billy

stevan apter — 2006-06-30 19:49:59

>
> > really, there aren't any recursive or looping primitives in F. just
> > 'i' and 'dip'.
>
> Nice :-). I like it.
>
i'm mulling your earlier comments, but i always have time to
respond to a compliment. :)

actually, all we need is 'dip', since 'i' is just [] swap dip. but
i wanted to keep ! for consistency with False, and to make the distinction
between F and Joy semantics clean.

stevan apter — 2006-06-30 20:29:06

----- Original Message -----
From: "William Tanksley, Jr" <wtanksleyjr@...>
To: <concatenative@yahoogroups.com>
Sent: Friday, June 30, 2006 2:51 PM
Subject: Re: [stack] Re: 'stack' and 'unstack' in F


> sa@... <sa@...> wrote:
> > > sa@... <sa@...> wrote:
> > > > > If you accept your proposed rule (that operating on a stack without
> > > > > enough values is a valid no-op), every primitive that takes any
> > > > > arguments has variable valence. Am I missing something?
> > > > in one sense, i guess so: a primitive has valence n when there
> > > > are at least n items on the stack, else it has valence 0/charge 0.
> > > > alternatively, you could say that every primitive has valence n,
> > > > but that "+" (any primitive) is the identity function when there
> > > > are fewer than n items on the stack.
>
> > > So what am I missing? It looks like you're agreeing with me that every
> > > primitive in your proposed system has variable valence (and charge).
> > > Your "alternatively" phrase is a bit confusing -- are you trying to
> > > claim that because a function's valence is constant, a primitive that
> > > implements two different functions therefore has a constant valence?
>
> > yeah, that's what i was trying to do, but i guess i didn't
> > get away with it. :)
>
> Yup, you're a sneaky guy, always trying to get away with things :-).
> Good thing I'm here to keep an eye on you.

that is reassuring.

>
> > seriously though, i think i can claim that F has 30 primitives,
> > 29 of which are associated with symbols, and one of which --
> > the identity function -- has no symbol; and that a symbol
> > denotes the function with which it is associated unless the
> > stack is short, in which case it denotes the identity function.
>
> So when you used the word 'function' earlier, you actually meant
> 'primitive', and when you used 'primitive' you meant 'predefined
> symbol'. I don't have any problem with that claim, but it doesn't seem
> to add anything to the discussion -- it seems like a mere search and
> replace, with no change to the logic or conclusions.

yes, i haven't been as precise as i should have been. F has functions,
but every one of them is a primitive, and vice-versa, and every
primitive lives on a symbol (i.e. not a digit or a character or
a square bracket or a blank), and every symbol is a primitive.

>
> In either case, the programs users type in will have to deal with
> variable valence whether they want to or not. A language using DEPTH
> will only have to deal with it only when it actually appears in the
> text.

i also regret introducing both the terms 'variable' and 'infinite',
and then muddling them. as i understand it, a function has variable
valence if it can take one or two or three or .. arguments; and a
function has infinite valence if it takes the entire stack as a
single argument. DEPTH has infinite valence. variable-valence
functions are, i think, sometimes called "multigrade functions,"
and i can't imagine why one would ever want them in a concatenative
language.

i just wanted to avoid having any functions which could see the
entire stack as a single object.

stevan apter — 2006-06-30 20:35:13

----- Original Message -----
From: "William Tanksley, Jr" <wtanksleyjr@...>
To: <concatenative@yahoogroups.com>
Sent: Friday, June 30, 2006 2:23 PM
Subject: Re: [stack] Re: 'stack' and 'unstack' in F


> sa@... <sa@...> wrote:
> > > > i invoked these concepts in order to explain my decision to
> > > > replace 'stack underflow error' with a different, non-error
> > > > behavior. i still think it's remarkable that 'stack' and
> > > > 'unstack' (whatever you may think of them) depend on this
> > > > behavior, and that replicating the behavior of certain joy
> > > > combinators (whatever you think of them) appears to require
> > > > these programs.
> > >
> > > This may be faulty logic. It's true that Joy combinators require those
> > > programs, and so if you're precisely imitating Joy you need stack and
> > > unstack; but it's not true that stack or unstack REQUIRE the behavior
> > > we're discussing. There are many alternates -- for example, a
> > > conditional version of ! or converge that will act the way you're
> > > describing, or a primitive that returns the current stack depth.
>
> > well, what i actually said was that 'stack' could be defined with
> > my method, but absent DEPTH or some equivalent primitive of variable
> > valence, it couldn't.
>
> That statement is neither useful nor true. (No offense meant, just
> good clean fun.)
>
> I'll prove by counterexample that it's untrue: I can define a language
> in which 'stack' could be defined without any variable valence
> primitives -- not even a variable loop. This hypothetical language has
> a stack that's a constant depth (probably a circular buffer), so
> saving it to an array requires a constant number of operations. This
> is an extreme counterexample, and I suspect that some gentler one
> probably exists -- but at the same time, note that one concatenative
> language actually works this way (Chuck Moore's machine forth and
> colorforth).

wow - a circular stack - a stircle.

>
> The reason I said that your statement wasn't useful is that you said
> "stack can be defined with my method" (which is true), and then "stack
> can not be defined without primitives of variable valence" (since your
> solution requires all primitives to have variable valence, it also
> excludes your method). So therefore even if we assume that the second
> statement is true, it doesn't say anything about my solution that it
> doesn't also say about yours.
>
> I just thought of a gentler counterexample: a primitive named 'empty?'
> could be used to implement 'stack'. It returns true when the stack is
> empty, false otherwise. Its valence is undetectably close to zero,
> because it doesn't actually consume anything; its charge is a constant
> 1. (It's not as good of a counterexample as my previous one, because
> it may be argued that its valence is 1 when the stack's full and 0
> when the stack's empty -- but that would be a pretty subtle argument.)

'empty?' takes the entire stack as an argument, returns 1 if it's
empty, else 0. so in my lingo, it has infinite valence.

>
> > > The problem with your suggestion is that it's a whole-language change;
> > > it can be used anywhere invisibly, and in fact is easy to use
> > > accidentally. In fact, it's way easier to use accidentally than it is
> > > to use on purpose, except for the single purpose you propose
> > > (consuming the entire stack).
>
> > actually, i rely on the behavior for 'unstack' as well, but
> > otherwise what you say is probably right. and i'm not sure
> > it doesn't have other applications.
>
> You're right: one possible application would be using your behavior to
> define DEPTH :-). My point was just that misusing it is very easy and
> invisible (you can't test a program by running it at the command line,
> because it might behave differently 'in vivo'), and using it correctly
> is hard for any job that doesn't naturally start with "first, consume
> the entire stack". In fact, every time you deliberately use it, you
> must start by first consuming the entire stack (and then possibly put
> some of it back).

well, the same could be said of many of joy's combinators. i
just don't see this as a compelling argument.

>
> -Billy
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>

William Tanksley, Jr — 2006-06-30 20:57:43

stevan apter <sa@...> wrote:
> i'm mulling your earlier comments, but i always have time to
> respond to a compliment. :)

Grin.

> actually, all we need is 'dip', since 'i' is just [] swap dip. but
> i wanted to keep ! for consistency with False, and to make the distinction
> between F and Joy semantics clean.

IMO you did the right thing. I like the result.

-Billy

William Tanksley, Jr — 2006-06-30 21:19:21

stevan apter <sa@...> wrote:
> > I'll prove by counterexample that it's untrue: I can define a language
> > in which 'stack' could be defined without any variable valence
> > primitives -- not even a variable loop. This hypothetical language has
> > a stack that's a constant depth (probably a circular buffer), so
> > saving it to an array requires a constant number of operations. This
> > is an extreme counterexample, and I suspect that some gentler one
> > probably exists -- but at the same time, note that one concatenative
> > language actually works this way (Chuck Moore's machine forth and
> > colorforth).

> wow - a circular stack - a stircle.

Heh, yup. No suprise that Chuck would have used one -- engineers are
always using circular buffers. (schtuffers?)

> > I just thought of a gentler counterexample: a primitive named 'empty?'
> > could be used to implement 'stack'. It returns true when the stack is
> > empty, false otherwise. Its valence is undetectably close to zero,
> > because it doesn't actually consume anything; its charge is a constant
> > 1. (It's not as good of a counterexample as my previous one, because
> > it may be argued that its valence is 1 when the stack's full and 0
> > when the stack's empty -- but that would be a pretty subtle argument.)

> 'empty?' takes the entire stack as an argument, returns 1 if it's
> empty, else 0. so in my lingo, it has infinite valence.

In your lingo, then, every one of your primitives has infinite valence
-- they also take the entire stack as an argument and branch based on
whether it's full or empty. Right?

> > You're right: one possible application would be using your behavior to
> > define DEPTH :-). My point was just that misusing it is very easy and
> > invisible (you can't test a program by running it at the command line,
> > because it might behave differently 'in vivo'), and using it correctly
> > is hard for any job that doesn't naturally start with "first, consume
> > the entire stack". In fact, every time you deliberately use it, you
> > must start by first consuming the entire stack (and then possibly put
> > some of it back).

> well, the same could be said of many of joy's combinators.

I don't like the behavior of Joy's combinators, and have argued so. I
don't like how they insist on saving and restoring the stack "behind
my back." But my argument above doesn't apply to Joy's combinators,
because they're just library functions, not a fundamental language
feature.

> i just don't see this as a compelling argument.

You don't have to define Joy's problems into your language. You can
implement a single primitive, DEPTH, that allows you to pretend to be
Joyful, but in reality your language is being False.

Seriously: Joy's combinators, unlike your language feature, are just
library functions. If I don't like how 'if' saves and restores the
stack (and I don't), I don't have to use it -- I can define my own
'if', and I'd be certain that my programs would work the way I wanted.
If I don't like how F's primitives behave, that's too bad -- I can't
escape them (unless I stop using F).

-Billy

stevan apter — 2006-07-01 03:17:26

----- Original Message -----
From: "William Tanksley, Jr" <wtanksleyjr@...>
To: <concatenative@yahoogroups.com>
Sent: Friday, June 30, 2006 5:19 PM
Subject: Re: [stack] Re: 'stack' and 'unstack' in F


> stevan apter <sa@...> wrote:
> > > I'll prove by counterexample that it's untrue: I can define a language
> > > in which 'stack' could be defined without any variable valence
> > > primitives -- not even a variable loop. This hypothetical language has
> > > a stack that's a constant depth (probably a circular buffer), so
> > > saving it to an array requires a constant number of operations. This
> > > is an extreme counterexample, and I suspect that some gentler one
> > > probably exists -- but at the same time, note that one concatenative
> > > language actually works this way (Chuck Moore's machine forth and
> > > colorforth).
>
> > wow - a circular stack - a stircle.
>
> Heh, yup. No suprise that Chuck would have used one -- engineers are
> always using circular buffers. (schtuffers?)

so >r and r> rotate the stack left and right one position? a nice
way to store/retrieve things. (i'm almost kidding - i wouldn't expect
moore to do this .. but i would!)

>
> > > I just thought of a gentler counterexample: a primitive named 'empty?'
> > > could be used to implement 'stack'. It returns true when the stack is
> > > empty, false otherwise. Its valence is undetectably close to zero,
> > > because it doesn't actually consume anything; its charge is a constant
> > > 1. (It's not as good of a counterexample as my previous one, because
> > > it may be argued that its valence is 1 when the stack's full and 0
> > > when the stack's empty -- but that would be a pretty subtle argument.)
>
> > 'empty?' takes the entire stack as an argument, returns 1 if it's
> > empty, else 0. so in my lingo, it has infinite valence.
>
> In your lingo, then, every one of your primitives has infinite valence
> -- they also take the entire stack as an argument and branch based on
> whether it's full or empty. Right?

no, i don't think so. + has valence 2 - it only cares about the top
two items. 'empty?' has to have the entire stack, in order to count
the elements. i admit that i don't know how to reconcile this idea
with the slogan that everything is a function of stacks to stacks.
e.g. in my implementation, the function which dispatches the + code
calls a function, viz. {x+y} on the top two arguments of the stack.

>
> > > You're right: one possible application would be using your behavior to
> > > define DEPTH :-). My point was just that misusing it is very easy and
> > > invisible (you can't test a program by running it at the command line,
> > > because it might behave differently 'in vivo'), and using it correctly
> > > is hard for any job that doesn't naturally start with "first, consume
> > > the entire stack". In fact, every time you deliberately use it, you
> > > must start by first consuming the entire stack (and then possibly put
> > > some of it back).
>
> > well, the same could be said of many of joy's combinators.
>
> I don't like the behavior of Joy's combinators, and have argued so. I
> don't like how they insist on saving and restoring the stack "behind
> my back." But my argument above doesn't apply to Joy's combinators,
> because they're just library functions, not a fundamental language
> feature.
>
> > i just don't see this as a compelling argument.
>
> You don't have to define Joy's problems into your language. You can
> implement a single primitive, DEPTH, that allows you to pretend to be
> Joyful, but in reality your language is being False.

:)

for now, i'm not convinced that these are problems.

>
> Seriously: Joy's combinators, unlike your language feature, are just
> library functions. If I don't like how 'if' saves and restores the
> stack (and I don't), I don't have to use it -- I can define my own
> 'if', and I'd be certain that my programs would work the way I wanted.
> If I don't like how F's primitives behave, that's too bad -- I can't
> escape them (unless I stop using F).

well, you could always just change the script. There's one data-
structure (O) which associates symbols with functions, e.g.

("$" ;y[1;{(x;x)}]) / dup



>
> -Billy
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>

William Tanksley, Jr — 2006-07-01 08:00:16

stevan apter <sa@...> wrote:

I'll be gone for a week -- one final message:

> > stevan apter <sa@...> wrote:
> > Heh, yup. No suprise that Chuck would have used one -- engineers are
> > always using circular buffers. (schtuffers?)

> so >r and r> rotate the stack left and right one position? a nice
> way to store/retrieve things. (i'm almost kidding - i wouldn't expect
> moore to do this .. but i would!)

'drop' rotates the stack one element, of course... I don't know
whether there's a word to rotate it the other way.

> > > > I just thought of a gentler counterexample: a primitive named 'empty?'
> > > > could be used to implement 'stack'. It returns true when the stack is
> > > > empty, false otherwise. Its valence is undetectably close to zero,
> > > > because it doesn't actually consume anything; its charge is a constant
> > > > 1. (It's not as good of a counterexample as my previous one, because
> > > > it may be argued that its valence is 1 when the stack's full and 0
> > > > when the stack's empty -- but that would be a pretty subtle argument.)

> > > 'empty?' takes the entire stack as an argument, returns 1 if it's
> > > empty, else 0. so in my lingo, it has infinite valence.

> > In your lingo, then, every one of your primitives has infinite valence
> > -- they also take the entire stack as an argument and branch based on
> > whether it's full or empty. Right?

> no, i don't think so. + has valence 2 - it only cares about the top
> two items. 'empty?' has to have the entire stack, in order to count
> the elements. i admit that i don't know how to reconcile this idea
> with the slogan that everything is a function of stacks to stacks.
> e.g. in my implementation, the function which dispatches the + code
> calls a function, viz. {x+y} on the top two arguments of the stack.

What's going on? Why are ALL your words allowed to know how deep the
stack is, while my single smart word gets penalized for knowing? Your
words ALL have the function of 'empty?', except that they're more
complex (empty? doesn't change its stack behavior at all; your words
do).

> > You don't have to define Joy's problems into your language. You can
> > implement a single primitive, DEPTH, that allows you to pretend to be
> > Joyful, but in reality your language is being False.

> :)
> for now, i'm not convinced that these are problems.

That's cool. I think your proposal has problems much worse than Joy's,
but they seem so obvious that after all this talk I'm pretty certain
you understand them and don't see them as problems. That's okay, I
guess -- I don't see how they wouldn't cripple the language, but
whatever works.

> > Seriously: Joy's combinators, unlike your language feature, are just
> > library functions. If I don't like how 'if' saves and restores the
> > stack (and I don't), I don't have to use it -- I can define my own
> > 'if', and I'd be certain that my programs would work the way I wanted.
> > If I don't like how F's primitives behave, that's too bad -- I can't
> > escape them (unless I stop using F).

> well, you could always just change the script. There's one data-
> structure (O) which associates symbols with functions, e.g.
> ("$" ;y[1;{(x;x)}]) / dup

Please explain -- what does "change the script" do? And don't all your
functions behave specially when the stack's empty, so no matter what
you assign, it'll still behave the same way?

-Billy

stevan apter — 2006-07-01 12:51:03

----- Original Message -----
From: "William Tanksley, Jr" <wtanksleyjr@...>
To: <concatenative@yahoogroups.com>
Sent: Saturday, July 01, 2006 4:00 AM
Subject: Re: [stack] Re: 'stack' and 'unstack' in F


> stevan apter <sa@...> wrote:
>
> I'll be gone for a week -- one final message:
>
> > > stevan apter <sa@...> wrote:
> > > Heh, yup. No suprise that Chuck would have used one -- engineers are
> > > always using circular buffers. (schtuffers?)
>
> > so >r and r> rotate the stack left and right one position? a nice
> > way to store/retrieve things. (i'm almost kidding - i wouldn't expect
> > moore to do this .. but i would!)
>
> 'drop' rotates the stack one element, of course... I don't know
> whether there's a word to rotate it the other way.
>
> > > > > I just thought of a gentler counterexample: a primitive named 'empty?'
> > > > > could be used to implement 'stack'. It returns true when the stack is
> > > > > empty, false otherwise. Its valence is undetectably close to zero,
> > > > > because it doesn't actually consume anything; its charge is a constant
> > > > > 1. (It's not as good of a counterexample as my previous one, because
> > > > > it may be argued that its valence is 1 when the stack's full and 0
> > > > > when the stack's empty -- but that would be a pretty subtle argument.)
>
> > > > 'empty?' takes the entire stack as an argument, returns 1 if it's
> > > > empty, else 0. so in my lingo, it has infinite valence.
>
> > > In your lingo, then, every one of your primitives has infinite valence
> > > -- they also take the entire stack as an argument and branch based on
> > > whether it's full or empty. Right?
>
> > no, i don't think so. + has valence 2 - it only cares about the top
> > two items. 'empty?' has to have the entire stack, in order to count
> > the elements. i admit that i don't know how to reconcile this idea
> > with the slogan that everything is a function of stacks to stacks.
> > e.g. in my implementation, the function which dispatches the + code
> > calls a function, viz. {x+y} on the top two arguments of the stack.
>
> What's going on? Why are ALL your words allowed to know how deep the
> stack is, while my single smart word gets penalized for knowing? Your
> words ALL have the function of 'empty?', except that they're more
> complex (empty? doesn't change its stack behavior at all; your words
> do).

from the implementation's perspective, there are really only three
functions - x, y, and z. each of them takes two arguments - a valence n,
and a function f - and returns one result - a function. that anonymous
function takes the entire stack and the entire queue, pops n elements
from the stack, applies f, takes the result of that application, and
either (x) appends it to the stack as a unit, (y) appends the separate
elements of the result to the stack, or (z) prepends the separate
elements of the result to the queue (for further evaluation.) + is
an 'x' function - given two lists, it adds them and appends the
resultant list to the stack as a unit (remember that F has atomic
extension.) 'dup' is a 'y' function - it returns two elements which
are appended to the stack. 'i' is a 'z' function - it takes a
quotation, prepends the elements of the quotation to the queue, which
are then subsequently evaluated.

when i say that no primitive in F sees the entire stack, i mean for
example that + sees only the two elements which the anonymous function
produced by 'x' passes to it. the fact that the anonymous function --
the "dispatcher" -- sees the entire stack is a mere implementation
detail. in a "real" implementation, i would expect this to be
handled quite differently. but at a conceptual level, all of the
primitives can be said to have fixed, finite valence.

here's how i would modify my implementation to support DEPTH: every
primitive p has fixed, finite valence n, but the dispatcher calls
p with n+1 arguments, the first being the depth of the stack.
every primitive except DEPTH ignores that argument; DEPTH returns
it. here's DEPTH in the F implementation:

x[0;{x}]

n is 0, the primitive corresponding to + is the identity function.

>
> > > You don't have to define Joy's problems into your language. You can
> > > implement a single primitive, DEPTH, that allows you to pretend to be
> > > Joyful, but in reality your language is being False.
>
> > :)
> > for now, i'm not convinced that these are problems.
>
> That's cool. I think your proposal has problems much worse than Joy's,
> but they seem so obvious that after all this talk I'm pretty certain
> you understand them and don't see them as problems. That's okay, I
> guess -- I don't see how they wouldn't cripple the language, but
> whatever works.
>
> > > Seriously: Joy's combinators, unlike your language feature, are just
> > > library functions. If I don't like how 'if' saves and restores the
> > > stack (and I don't), I don't have to use it -- I can define my own
> > > 'if', and I'd be certain that my programs would work the way I wanted.
> > > If I don't like how F's primitives behave, that's too bad -- I can't
> > > escape them (unless I stop using F).
>
> > well, you could always just change the script. There's one data-
> > structure (O) which associates symbols with functions, e.g.
> > ("$" ;y[1;{(x;x)}]) / dup
>
> Please explain -- what does "change the script" do? And don't all your
> functions behave specially when the stack's empty, so no matter what
> you assign, it'll still behave the same way?

i mean you could e.g. add DEPTH by add the suggested code above
to the O data-structure.

>
> -Billy
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>

William Tanksley, Jr — 2006-07-12 00:36:05

stevan apter <sa@...> wrote:
[I said:]
> > No, you're totally right. This is why I also don't like Joy's habit of
> > saving and restoring the entire stack. I would rather define things
> > such that no full-stack saving and restoring is needed; most other
> > pre-Joy concatenative languages work that way, so there's no doubt it
> > can be done. (Of course, you're proving that you can do the same
> > things as Joy, so I accept that you must perform full-stack saving and
> > restoring.)

> forth and factor, to take two examples, have >r and r> (have i got
> their names right?), and possibly something like DEPTH as well, isn't
> that so? i think if you have those at your disposal, you have enough
> to define e.g. 'linrec'.

Yes, Forth has those; in spite of my admiration of Factor, I don't
know how it does those things. (BTW, the use of DEPTH is discouraged
in Forth.)

> i think we've discussed this in the past: in joy, you can have
> something like
> X [1 2 3][+]map
[which]
> leaves
> X [x y z]

> on the stack. how would you implement this behavior without some
> mechanism for saving and restoring the stack before and after each
> application of +?

You'd do it simply by not saving and restoring the stack, and thereby
expecting the programmer to code in a way that acts responsibly with
the stack. In your example, a general non-stack-saving replacement for
"[+]map" would be "[+]cons map" (this doesn't preserve the X value,
but that's very easy to do -- and if I don't want to preserve the X
value, I shouldn't have to). "[over+]map" is essentially the same
thing as your stack-preserving map, and "[X+]map" works with a loss of
generality.

In general, it's easy to come up with an equivalent program that
doesn't rely on stack-saving semantics. I can't think of a time when
it wouldn't be more efficient -- especially if you consider the
overhead of saving and restoring the stack. It would always be closer
to "linear logic" (a style of programming in which each given data
item is only used once unless it's explicitly copied), since by
definition implicitly saving the stack is performing an implicit copy.

The opposite is difficult. What if you want to write some complex
program that computes something by consuming values from the stack
until the result converges in some way? If your converge functions all
preserve the stack, by definition you can't use them to consume it. If
your converge functions don't preserve the stack, you can always write
the quotations you're going to pass to take care of their own stack
saving. "[over+]map" is an example of doing this. Because the
quotation doesn't _assume_ stack saving, it'll work no matter whether
map saves the stack or not.

Here's an excercise for you. Suppose I have a function in a Forthlike
Joy whose source is "[1 2 3]map". Obviously, this function consumes
three stack parameters and returns a list of three numbers. How would
you translate that into Joy (i.e. a language with a stack-preserving
"map"), without knowing or looking at the number of items in the list?

-Billy

sa@dfa.com — 2006-07-12 13:09:38

concatenative@yahoogroups.com wrote on 07/11/2006 08:36:05 PM:

> stevan apter <sa@...> wrote:
> [I said:]
> > > No, you're totally right. This is why I also don't like Joy's habit
of
> > > saving and restoring the entire stack. I would rather define things
> > > such that no full-stack saving and restoring is needed; most other
> > > pre-Joy concatenative languages work that way, so there's no doubt it
> > > can be done. (Of course, you're proving that you can do the same
> > > things as Joy, so I accept that you must perform full-stack saving
and
> > > restoring.)
>
> > forth and factor, to take two examples, have >r and r> (have i got
> > their names right?), and possibly something like DEPTH as well, isn't
> > that so? i think if you have those at your disposal, you have enough
> > to define e.g. 'linrec'.
>
> Yes, Forth has those; in spite of my admiration of Factor, I don't
> know how it does those things. (BTW, the use of DEPTH is discouraged
> in Forth.)
>
> > i think we've discussed this in the past: in joy, you can have
> > something like
> > X [1 2 3][+]map
> [which]
> > leaves
> > X [x y z]
>
> > on the stack. how would you implement this behavior without some
> > mechanism for saving and restoring the stack before and after each
> > application of +?
>
> You'd do it simply by not saving and restoring the stack, and thereby
> expecting the programmer to code in a way that acts responsibly with
> the stack. In your example, a general non-stack-saving replacement for
> "[+]map" would be "[+]cons map" (this doesn't preserve the X value,
> but that's very easy to do -- and if I don't want to preserve the X
> value, I shouldn't have to). "[over+]map" is essentially the same
> thing as your stack-preserving map, and "[X+]map" works with a loss of
> generality.

it's a little hard for me to tell, but i think my notation
might have led you astray. consider

10 20 30 [1 2 3][+]map
10 20 30 [31 32 33]

'map' saves the stack below [31 32 33] (i.e. 10 20 30). it
pushes 1, so the stack is 10 20 30 1, then executes +, leaving
10 20 31. it sets 31 aside, restores the stack to 10 20 30,
pushes 2, &c. finally, it has the results [31 32 33]; it
restores the stack once more, and pushes the result.

that's my understanding of how joy's 'map' works.

William Tanksley, Jr — 2006-07-12 19:26:28

sa@... <sa@...> wrote:
> it's a little hard for me to tell, but i think my notation
> might have led you astray. consider
> 10 20 30 [1 2 3][+]map
> 10 20 30 [31 32 33]

What did I do wrong? This looks like what I'd expect. Will one of my
functions fail to produce this result (aside from the one that throws
away the initial 30)?

> 'map' saves the stack below [31 32 33] (i.e. 10 20 30). it
> pushes 1, so the stack is 10 20 30 1, then executes +, leaving
> 10 20 31. it sets 31 aside, restores the stack to 10 20 30,
> pushes 2, &c. finally, it has the results [31 32 33]; it
> restores the stack once more, and pushes the result.

Yeah, that look like what I expected.

-Billy

stevan apter — 2006-07-12 22:59:53

i think what you're saying is this (i haven't checked to see
whether your functions do this):

the programmer should know that +, applied to each of [1 2 3],
requires an additional argument, which is to be found on the
stack. if the function were, say, +*, then each application
would require two additional arguments, which are to be found on
the stack. then, depending on the programmer's intentions, he
should explicitly do just that: collect the additional values
from the stack, and construct the appropriate list to map +
(or +*) on:

10 20 30 [[30 1][30 2][30 3]] ...

what's bad about joy's map is that it usurps this control from
the programmer.

if i've got this right, then there are two things i would say.

first, joy's map (and f's - but see below) doesn't require
that the programmer know anything about the function to be
mapped, or the nature of the items in the list on to which
the function is to be mapped.

.. [X Y Z][..] map

will consume as many items from the stack as [..] requires,
in addition to each of X, Y, Z. so

10 20 30 [1 [2 3][]][i+*]map
10 20 30 [620 150 500]

second, as you can see from this example, the function to
be mapped begins with 'i'. this is because (at least in
my implementation) the function to be mapped can be a list
function, like 'count', or an atomic function, like +*.
this is a minor point.

in F, i've implemented two sets of combinators, e.g. 'map'
and 'Map'. 'map' is weak: if the function doesn't find
as many values as it requires in each item of the list, it
fails with a stack underflow. 'Map' is like joy's 'map',
and in fact is defined using the weaker 'map'.

here are the definitions in F's joy dialect:

[[dup count [] top] dip swap [quote uncons dip dup top
[cons unit apply first swons] dipd] do pop pop rev] map

[quote stack dipd [quote unit map quote join right] dip
quote join left [unit apply last] map] Map

as you can see, 'Map' uses 'stack', 'map' does not.

nb:

quote x -> [x]
x y z top -> z x y
[x y z] rev -> [z y x]
x [y z w] f -> [x y f i x z f i x w f i]

'apply' is joy's 'infra'.

i think perhaps our differences on this matter (and possibly
others as well) can be reduced to this: i see joy and F as
high-level languages, while you see them as more forth-like.
in that case, you would give greater weight to considerations
which preoccupy forthians, mainly efficiency and programmer-
control.

----- Original Message -----
From: "William Tanksley, Jr" <wtanksleyjr@...>
To: <concatenative@yahoogroups.com>
Sent: Wednesday, July 12, 2006 3:26 PM
Subject: Re: [stack] Re: 'stack' and 'unstack' in F


> sa@... <sa@...> wrote:
> > it's a little hard for me to tell, but i think my notation
> > might have led you astray. consider
> > 10 20 30 [1 2 3][+]map
> > 10 20 30 [31 32 33]
>
> What did I do wrong? This looks like what I'd expect. Will one of my
> functions fail to produce this result (aside from the one that throws
> away the initial 30)?
>
> > 'map' saves the stack below [31 32 33] (i.e. 10 20 30). it
> > pushes 1, so the stack is 10 20 30 1, then executes +, leaving
> > 10 20 31. it sets 31 aside, restores the stack to 10 20 30,
> > pushes 2, &c. finally, it has the results [31 32 33]; it
> > restores the stack once more, and pushes the result.
>
> Yeah, that look like what I expected.
>
> -Billy
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>

William Tanksley, Jr — 2006-07-13 19:24:30

stevan apter <sa@...> wrote:
> i think what you're saying is this (i haven't checked to see
> whether your functions do this):

> the programmer should know that +, applied to each of [1 2 3],
> requires an additional argument, which is to be found on the
> stack.

Hmm... Yes, the programmer should (and does) know this, and must know
it for either of our models.

> if the function were, say, +*, then each application
> would require two additional arguments, which are to be found on
> the stack. then, depending on the programmer's intentions, he
> should explicitly do just that: collect the additional values
> from the stack, and construct the appropriate list to map +
> (or +*) on:
> 10 20 30 [[30 1][30 2][30 3]] ...

I don't see why you'd ever want to do that. Parsing through the data
is map's job. You want to pass a quotation to map that does what map
needs. One thing that will work elegantly in this case is to take a
quotation that takes two values and curry a constant value onto it to
get a new quotation that only takes one value. That's simple, commonly
used in functional programming, and fairly fast.

> what's bad about joy's map is that it usurps this control from
> the programmer.

Not really. Not only do I not see that as useful control, I'm also not
so much worried about any possible functionality loss -- anything one
system can do, the other system can do perhaps a little less comfort.
The real question in any approach to programming language design is
"what operations does this design make comfortable?" If you're
thinking of adding GOTO to a pure structural language, it'll perhaps
make some things much more comfortable -- but at what cost to the
language?

By the way, I believe that every function that uses a stack-restoring
'map' can be written to work independantly of the stack-restoring
behavior of 'map'. (Proof by lassitude: I haven't been able to find a
counterexample.) I'm certain that some of them will run slower, of
course.

So there's no loss of control either way. I think the main
consideration is one of style and safety.

In terms of safety, stack-restoring means that some operations will
have an effect when used in some locations that they won't when used
in other locations. This removes referential transparency in a very
odd way, for no obvious gain.

In terms of style, stack-restoring encourages code that depends on it;
for obvious example, one would write a map function that, if 'i' were
used instead of map to execute it on an arbitrary element of the list,
would have a different result than what would be seen by examining the
equivalent element of the result list from map.

For example,
[1 2 3][+]map head
is not the same stack effect as
[1 2 3]head [+] i

Worse problems can be seen if [+] is replaced with an arbitrary quotation.

It seems clear to me that the best style is to use static stack
effects whenever possible -- even 'i' can be staticly typesafe if the
programmer specifies what type of quotation is passed to it.

The only substantial advantage given by stack-saving combinators like
Joy's map, if, and so on is to people who are writing non-static stack
effects, because non-static stack effects are hard to clean up with
anything less than a full stack save and restore. And that behavior is
not something we have any need to make easier.

> if i've got this right, then there are two things i would say.

> first, joy's map (and f's - but see below) doesn't require
> that the programmer know anything about the function to be
> mapped, or the nature of the items in the list on to which
> the function is to be mapped.

Same for my 'map'. You should add that the person writing the
quotation being passed to 'map' does have to know the nature of the
items on the list being passed to 'map', but shouldn't have to know
anything about the environment 'map' is being called in.

If the programmer(s) adhere to these guidelines, then it won't matter
whether 'map' saves and/or restores the stack, because the passed
function will only access the data it knows is there.

> .. [X Y Z][..] map
> will consume as many items from the stack as [..] requires,
> in addition to each of X, Y, Z. so

That's a very unclear description of what happens -- it actually won't
consume anything from the stack, because the stuff is still there on
the stack when it finishes. In my recommended version of map it would
consume the stuff, of course. In Joy's it would only be able to look
at the stuff, and only as much stuff as a single execution of [..]
would require.

Do you not find that confusing?

> 10 20 30 [1 [2 3][]][i+*]map
> 10 20 30 [620 150 500]

> second, as you can see from this example, the function to
> be mapped begins with 'i'. this is because (at least in
> my implementation) the function to be mapped can be a list
> function, like 'count', or an atomic function, like +*.
> this is a minor point.

What's your point? I see that the function begins with 'i', but I
don't know what I'm being asked to conclude from that. I can't imagine
an implementation of 'map' that disallowed list functions -- that
would seem pointless.

> in F, i've implemented two sets of combinators, e.g. 'map'
> and 'Map'. 'map' is weak: if the function doesn't find
> as many values as it requires in each item of the list, it
> fails with a stack underflow. 'Map' is like joy's 'map',
> and in fact is defined using the weaker 'map'.

Yes, your 'map' meets all my requirements and then some. It actually
seems to me to act more like a typical applicative version of 'map'.

> i think perhaps our differences on this matter (and possibly
> others as well) can be reduced to this: i see joy and F as
> high-level languages, while you see them as more forth-like.

Nope. It's an issue of language design, not as high or low level.
Joy's stack shenanigans are like Fortran's computed GOTOs -- nice to
have, but IFs really are better language design.

(And obviously, you see Forth as a low-level language, while I see it
as the framework on which an application-specific concatenative
language is built. Make no mistake, Forth offers fewer amenities than
Joy -- but that doesn't make it the opposite of high-level.)

> in that case, you would give greater weight to considerations
> which preoccupy forthians, mainly efficiency and programmer-
> control.

Forthians do have something to teach concatenativians. You can't
dismiss everything we have to say as being merely about speed
concerns. This one isn't even peripherially about speed. It's about
safety, style, and predictability -- hmm, and reusability.

-Billy

stevan apter — 2006-07-23 17:54:09

i don't know why it took me so long to realize that 'stack',
'unstack', 'r>' and '>r' are instances of a more generalized
'cont' operation.

$, the 'cont' combinator, expects a program p on top of the stack.
p expects two quotations q and s. q is the current queue, and
beneath that, s is the current stack minus p. p leaves s' and q'
on the stack, which are made the new stack and queue:

s^p^$ q -> s' q'

s^q^p -> s'^q'

then:

J>'stack"o
[[[dup unit join]
dip]
cont]

J>'unstack"o
[['last dip]
cont]

J>'queue"o
[[[rev uncons rev swap]
dip swap unit join]
cont]

J>'unqueue"o
[[rev uncons rev [
unit join]
dip]
cont]

'cont' is general, in that it puts the future evolution of the
program state (the stack and the queue) directly under the control
of the programmer.

for K fans, 'cont' is defined:

{x,e[x;(-1_ y;z);*-1#y]1}




[Non-text portions of this message have been removed]

John Cowan — 2006-07-23 18:19:52

stevan apter scripsit:

> i don't know why it took me so long to realize that 'stack',
> 'unstack', 'r>' and '>r' are instances of a more generalized
> 'cont' operation.

Welcome to Scheme (SML, Haskell).

--
John Cowan <cowan@...>
Yakka foob mog. Grug pubbawup zink wattoom gazork. Chumble spuzz.
-- Calvin, giving Newton's First Law "in his own words"

William Tanksley, Jr — 2006-07-23 19:33:35

stevan apter <sa@...> wrote:
> i don't know why it took me so long to realize that 'stack',
> 'unstack', 'r>' and '>r' are instances of a more generalized
> 'cont' operation.

Sounds very interesting. Could you run that by me one more time? I
don't get it at all.

-Billy

stevan apter — 2006-07-23 20:32:08

$ requires that the program leave two quotations on the stack.
for example,

1 2 3 'id $ 4 5 6

contemplate the trace:

J>1 2 3 'id $ 4 5 6
? 1 2 3 'id $ 4 5 6
1 ? 2 3 'id $ 4 5 6
1 2 ? 3 'id $ 4 5 6
1 2 3 ? 'id $ 4 5 6
1 2 3 'id ? $ 4 5 6
[1 2 3] [4 5 6] ? id
[1 2 3] [4 5 6] ? [] !
[1 2 3] [4 5 6] [] ? !
[1 2 3] [4 5 6] ?
1 2 3 ? 4 5 6
1 2 3 4 ? 5 6
1 2 3 4 5 ? 6
1 2 3 4 5 6 ?

when $ evaluates, the stack is 1^2^3^[id] and the queue is 4^5^6.
$ packages up the stack and queue as two quotations, then evaluates
the program, in this case [id]. the result is a new stack and a
new queue.

now consider 'stack', which is defined:

[[[|unit,]`]$] stack

that is,

[[[dup unit join] dip] cont] stack

the stack trace:

J>1 2 3 stack 4 5 6
? 1 2 3 stack 4 5 6
1 ? 2 3 stack 4 5 6
1 2 ? 3 stack 4 5 6
1 2 3 ? stack 4 5 6
1 2 3 ? [[| unit ,] `] $ 4 5 6
1 2 3 [[| unit ,] `] ? $ 4 5 6
[1 2 3] [4 5 6] ? [| unit ,] `
[1 2 3] [4 5 6] [| unit ,] ? `
[1 2 3] ? | unit , [4 5 6]
[1 2 3] [1 2 3] ? unit , [4 5 6]
[1 2 3] [1 2 3] ? ' ' ` , [4 5 6]
[1 2 3] [1 2 3] '' ? ` , [4 5 6]
[1 2 3] ? ' [1 2 3] , [4 5 6]
[1 2 3] '[1 2 3] ? , [4 5 6]
[1 2 3 [1 2 3]] ? [4 5 6]
[1 2 3 [1 2 3]] [4 5 6] ?
1 2 3 [1 2 3] ? 4 5 6
1 2 3 [1 2 3] 4 ? 5 6
1 2 3 [1 2 3] 4 5 ? 6
1 2 3 [1 2 3] 4 5 6 ?



----- Original Message -----
From: William Tanksley, Jr
To: concatenative@yahoogroups.com
Sent: Sunday, July 23, 2006 3:33 PM
Subject: Re: [stack] Re: 'stack' and 'unstack' in F


stevan apter <sa@...> wrote:
> i don't know why it took me so long to realize that 'stack',
> 'unstack', 'r>' and '>r' are instances of a more generalized
> 'cont' operation.

Sounds very interesting. Could you run that by me one more time? I
don't get it at all.

-Billy




Yahoo! Groups Links








[Non-text portions of this message have been removed]

William Tanksley, Jr — 2006-07-23 21:11:24

stevan apter <sa@...> wrote:
> $ requires that the program leave two quotations on the stack.
> for example,
> contemplate the trace:

Thank you, that was exactly what I needed. So $ takes a function,
wraps up the stack and the rest-of-the-program for it into 2 lists,
and the function is then able to manipulate those lists however it
wishes. When the function returns, $ finishes by restoring the stack
and queue from those 2 lists.

I'm not sure I agree with you on the elegance of this $ operator. It
does a LOT, wouldn't you say? It doesn't so much seem to replace the
stack operator as incorporate (or require) it, since one of the many
things it has to do is push the stack into a list.

On the other hand, the $ operator does seem to do the same thing for
stack/unstack that the dip operator does for >R/R>: it removes the
need to track the pair of operators, and therefore makes it impossible
to "get it wrong".

I think I'd be happier with a different breakdown, though: one
operator to do that with the stack, and another operator to do it with
the queue.

-Billy

stevan apter — 2006-07-24 11:22:36

of course you're right that $ incorporates 'stack', and if
that was all that was achieved it would be of little interest.
but it goes well beyond that, since it gathers together under
one primitive all operations from whole program state to whole
program state.

----- Original Message -----
From: William Tanksley, Jr
To: concatenative@yahoogroups.com
Sent: Sunday, July 23, 2006 5:11 PM
Subject: Re: [stack] Re: 'stack' and 'unstack' in F


stevan apter <sa@...> wrote:
> $ requires that the program leave two quotations on the stack.
> for example,
> contemplate the trace:

Thank you, that was exactly what I needed. So $ takes a function,
wraps up the stack and the rest-of-the-program for it into 2 lists,
and the function is then able to manipulate those lists however it
wishes. When the function returns, $ finishes by restoring the stack
and queue from those 2 lists.

I'm not sure I agree with you on the elegance of this $ operator. It
does a LOT, wouldn't you say? It doesn't so much seem to replace the
stack operator as incorporate (or require) it, since one of the many
things it has to do is push the stack into a list.

On the other hand, the $ operator does seem to do the same thing for
stack/unstack that the dip operator does for >R/R>: it removes the
need to track the pair of operators, and therefore makes it impossible
to "get it wrong".

I think I'd be happier with a different breakdown, though: one
operator to do that with the stack, and another operator to do it with
the queue.

-Billy



Yahoo! Groups Links







[Non-text portions of this message have been removed]

William Tanksley, Jr — 2006-07-24 13:42:10

stevan apter <sa@...> wrote:
> of course you're right that $ incorporates 'stack', and if
> that was all that was achieved it would be of little interest.
> but it goes well beyond that, since it gathers together under
> one primitive all operations from whole program state to whole
> program state.

I see; that does seem to be of interest. With $, you don't need any
kind of hidden access to "magic" variables like DEPTH or IP. By the
way, is $ mnemonic for $tate (state)?

Whoops, there's still another magic variable -- the dictionary. If $
is supposed to truly provide the whole program state, it must also
provide access to the dictionary. The most logical way is no doubt as
another list, probably a list of pairs.

I'm not conceding that I like $, by the way ;-). I'm just admitting
that I see your point in defining it (I think).

-Billy

sa@dfa.com — 2006-07-24 17:00:56

concatenative@yahoogroups.com wrote on 07/24/2006 09:42:10 AM:

> stevan apter <sa@...> wrote:
> > of course you're right that $ incorporates 'stack', and if
> > that was all that was achieved it would be of little interest.
> > but it goes well beyond that, since it gathers together under
> > one primitive all operations from whole program state to whole
> > program state.
>
> I see; that does seem to be of interest. With $, you don't need any
> kind of hidden access to "magic" variables like DEPTH or IP. By the
> way, is $ mnemonic for $tate (state)?

yes, because i can't type the cent character, which would be mnemonic
for "continuation". :)

>
> Whoops, there's still another magic variable -- the dictionary. If $
> is supposed to truly provide the whole program state, it must also
> provide access to the dictionary. The most logical way is no doubt as
> another list, probably a list of pairs.

yes, the program state = [env stack queue], but since F doesn't
permit reassignment, there's no point in making env part of the
continuation. that is, the access F programs have to the environment
is limited to (i) first-time assignment of a name to a value, and (ii)
use of a variable.

>
> I'm not conceding that I like $, by the way ;-). I'm just admitting
> that I see your point in defining it (I think).
>
> -Billy
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>

William Tanksley, Jr — 2006-07-24 17:35:29

sa@... <sa@...> wrote:
> > I see; that does seem to be of interest. With $, you don't need any
> > kind of hidden access to "magic" variables like DEPTH or IP. By the
> > way, is $ mnemonic for $tate (state)?

> yes, because i can't type the cent character, which would be mnemonic
> for "continuation". :)

Oh, right -- it's kinda like "call/cc". Hmm. Yes, I guess it is,
kinda. (The name "continuation" doesn't communicate to me -- I now see
that's what you meant by "cont" in your original post.)

Technically, all "call/cc" needs is to pass the queue to the called
function -- stacking and unstacking the entire stack is needless,
since the entire stack will be passed to the function no matter what,
and the function is free to manipulate the resulting stack in any way.
The _only_ reasons I can see for doing it is that you either want to
implement stack/unstack in terms of $, or you want to allow
cooperative multitasking. The first one seems like an intrinsicly bad
idea; the second one seems just as well done by providing three
state-fetching words instead of just one (fetch-stack, fetch-queue,
fetch-dictionary), because each of those three words can be used in
other ways without requiring the overhead of the other two fetches and
restores.

I guess what I'm saying is that if you want to implement call/cc you
should be doing less (only pass the queue), and if you want to save
the entire execution state you should be doing more (also pass the
dictionary).

> > Whoops, there's still another magic variable -- the dictionary. If $
> > is supposed to truly provide the whole program state, it must also
> > provide access to the dictionary. The most logical way is no doubt as
> > another list, probably a list of pairs.

> yes, the program state = [env stack queue], but since F doesn't
> permit reassignment, there's no point in making env part of the
> continuation. that is, the access F programs have to the environment
> is limited to (i) first-time assignment of a name to a value, and (ii)
> use of a variable.

I'm not sure about this argument -- you're arguing that giving direct
access to the dictionary would allow you to do things that you
couldn't do without the direct access, which seems to be an argument
in *favor* of the direct access.

-Billy

stevan apter — 2006-07-24 21:15:26

----- Original Message -----
From: "William Tanksley, Jr" <wtanksleyjr@...>
To: <concatenative@yahoogroups.com>
Sent: Monday, July 24, 2006 1:35 PM
Subject: Re: [stack] Re: 'stack' and 'unstack' in F


> sa@... <sa@...> wrote:
> > > I see; that does seem to be of interest. With $, you don't need any
> > > kind of hidden access to "magic" variables like DEPTH or IP. By the
> > > way, is $ mnemonic for $tate (state)?
>
> > yes, because i can't type the cent character, which would be mnemonic
> > for "continuation". :)
>
> Oh, right -- it's kinda like "call/cc". Hmm. Yes, I guess it is,
> kinda. (The name "continuation" doesn't communicate to me -- I now see
> that's what you meant by "cont" in your original post.)

i've renamed it 'state', which seems better on all accounts.

>
> Technically, all "call/cc" needs is to pass the queue to the called
> function -- stacking and unstacking the entire stack is needless,
> since the entire stack will be passed to the function no matter what,
> and the function is free to manipulate the resulting stack in any way.
> The _only_ reasons I can see for doing it is that you either want to
> implement stack/unstack in terms of $,

yes

or you want to allow
> cooperative multitasking. The first one seems like an intrinsicly bad
> idea;


i don't see why.


the second one seems just as well done by providing three
> state-fetching words instead of just one (fetch-stack, fetch-queue,
> fetch-dictionary), because each of those three words can be used in
> other ways without requiring the overhead of the other two fetches and
> restores.
>
> I guess what I'm saying is that if you want to implement call/cc you
> should be doing less (only pass the queue), and if you want to save
> the entire execution state you should be doing more (also pass the
> dictionary).

i might be driven in that direction if there was any cost attached
to placing the stack or the queue on the stack, but since both are
lists, the operation is free. (so too is setting the stack and queue)
i recognize that this might not be true given a different implementation,
but for me it's not a consideration. where x is a list x,,x -- appending
the unit-list of x to x -- costs nothing.

>
> > > Whoops, there's still another magic variable -- the dictionary. If $
> > > is supposed to truly provide the whole program state, it must also
> > > provide access to the dictionary. The most logical way is no doubt as
> > > another list, probably a list of pairs.
>
> > yes, the program state = [env stack queue], but since F doesn't
> > permit reassignment, there's no point in making env part of the
> > continuation. that is, the access F programs have to the environment
> > is limited to (i) first-time assignment of a name to a value, and (ii)
> > use of a variable.
>
> I'm not sure about this argument -- you're arguing that giving direct
> access to the dictionary would allow you to do things that you
> couldn't do without the direct access, which seems to be an argument
> in *favor* of the direct access.

in f, assignment of a name to a value (i.e. creating a variable) has
the syntax

value undefined-name

and use of a variable has the syntax

defined-name

there's no way to un-assign a name (i.e. undefine a variable.)

if i were to provide direct access to the environment, i would have
to support first-class dictionaries or the equivalent. i don't want
to do the former. an equivalent might be, as you suggest, a pair
whose first element was a list of names and whose second was a list
of variables. then, given 'env' and 'unenv', i could define 'set',
'get', and 'del'.

it's a thought.

>
> -Billy
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>

William Tanksley, Jr — 2006-07-24 22:15:43

stevan apter <sa@...> wrote:
> > The _only_ reasons I can see for doing it is that you either want to
> > implement stack/unstack in terms of $,
[...]
> > seems like an intrinsicly bad idea;

> i don't see why.

I could be wrong :-). I think my dislike for dip-like solutions is
betraying me, honestly. (dip provides you with a second stack, but
doesn't let you manage it, thereby making it impossible to screw it up
-- a good thing and a sometimes inconvenient thing both at the same
time.)

> > I guess what I'm saying is that if you want to implement call/cc you
> > should be doing less (only pass the queue), and if you want to save
> > the entire execution state you should be doing more (also pass the
> > dictionary).

> i might be driven in that direction if there was any cost attached
> to placing the stack or the queue on the stack, but since both are
> lists, the operation is free. (so too is setting the stack and queue)
> i recognize that this might not be true given a different implementation,
> but for me it's not a consideration. where x is a list x,,x -- appending
> the unit-list of x to x -- costs nothing.

That's a really good point. It's good to have an operation that's
mathematically clean and very computationally inexpensive.

> if i were to provide direct access to the environment, i would have
> to support first-class dictionaries or the equivalent. i don't want
> to do the former. an equivalent might be, as you suggest, a pair
> whose first element was a list of names and whose second was a list
> of variables. then, given 'env' and 'unenv', i could define 'set',
> 'get', and 'del'.

> it's a thought.

Alternately, you could say "the third stack value is the dictionary,
not currently supported. Don't mess with it." :-).

-Billy

stevan apter — 2006-07-25 10:36:08

----- Original Message -----
From: "William Tanksley, Jr" <wtanksleyjr@...>
To: <concatenative@yahoogroups.com>
Sent: Monday, July 24, 2006 6:15 PM
Subject: Re: [stack] Re: 'stack' and 'unstack' in F


> stevan apter <sa@...> wrote:
> > > The _only_ reasons I can see for doing it is that you either want to
> > > implement stack/unstack in terms of $,
> [...]
> > > seems like an intrinsicly bad idea;
>
> > i don't see why.
>
> I could be wrong :-). I think my dislike for dip-like solutions is
> betraying me, honestly. (dip provides you with a second stack, but
> doesn't let you manage it, thereby making it impossible to screw it up
> -- a good thing and a sometimes inconvenient thing both at the same
> time.)

i've never used (or implemented) a return stack in any of my toy
languages, and in fact i'm not entirely clear on how it works in
forth. in F, 'dip' swaps the first two items on the stack and
pushes them onto the queue, where first the program will evaluate,
and then the element which was formerly on top of the stack will
evaluate.

a pattern i find myself using repeatedly is a combination of 'dip',
'pair' (to shorten the stack), 'bot' and 'top' (i.e. 'rot' and '-rot'),
and 'unpair' (to unpack paired elements.) it's just too painful to
try to work with more than four or five stack elements.

with the 'queue' and 'unqueue' words (i.e. 'r>' and '>r'), i have
another method for temporarily moving things out of the way -- using
the far end of the queue as a temporary holding place for elements
on top of the stack. (and as we know 'dip' can then be defined as
'queue ! unqueue'.) as you say, this mechanism is prone to error
through carelessness.

btw, are you familiar with reva-forth? if so, do you have any
opinions?

William Tanksley, Jr — 2006-07-25 18:51:51

stevan apter <sa@...> wrote:
> From: "William Tanksley, Jr" <wtanksleyjr@...>
> > I could be wrong :-). I think my dislike for dip-like solutions is
> > betraying me, honestly. (dip provides you with a second stack, but
> > doesn't let you manage it, thereby making it impossible to screw it up
> > -- a good thing and a sometimes inconvenient thing both at the same
> > time.)

> i've never used (or implemented) a return stack in any of my toy
> languages, and in fact i'm not entirely clear on how it works in
> forth. in F, 'dip' swaps the first two items on the stack and
> pushes them onto the queue, where first the program will evaluate,
> and then the element which was formerly on top of the stack will
> evaluate.

Forth's return stack is vaguely like your queue, except that the
return stack doesn't hold anything from the currently executing word.
That information is maintained in the IP (instruction pointer).
Because Forth doesn't include any currently executing information on
the return stack, manipulation of the return stack won't affect the
current word's execution, only the execution of words after the
current word returns.

> a pattern i find myself using repeatedly is a combination of 'dip',
> 'pair' (to shorten the stack), 'bot' and 'top' (i.e. 'rot' and '-rot'),
> and 'unpair' (to unpack paired elements.) it's just too painful to
> try to work with more than four or five stack elements.

Forth doesn't have list manipulations, but if it did most Forthers
would discourage their use for the purpose of stack manipulations. The
#1 best way to not suffer the pain of dealing with large numbers of
stack elements is to design so that you don't need large numbers of
stack elements :-); after that, use the return stack/queue. I
personally would prefer the stack manipulation sublanguage (I've
mentioned it to you before) to using the return stack; it's much more
self-documenting, and usually faster.

> with the 'queue' and 'unqueue' words (i.e. 'r>' and '>r'), i have
> another method for temporarily moving things out of the way -- using
> the far end of the queue as a temporary holding place for elements
> on top of the stack. (and as we know 'dip' can then be defined as
> 'queue ! unqueue'.) as you say, this mechanism is prone to error
> through carelessness.

So's anything that changes the future execution, really -- I shouldn't
have been critical. It's a fine solution. The *exact* equivalent of
Forth's >R word would be a word that searched to find the end of the
current word, and popped the TOS into that location in the queue. R>
would do the opposite. Of course, if you really wanted perfect
imitation of unchecked Forth, you'd also have to include a word that
crashed the runtime if the >R position on the queue was reached before
a R> was executed -- but somehow I suspect you won't see that as a
positive feature ;-). (It's not positive, it's just simple and fast.)

> btw, are you familiar with reva-forth? if so, do you have any
> opinions?

I've seen it before. Looking again, I like it -- it's simple and
clean, a nice representative Forth. It imitates some of the best
recent Forths, without going as far as colorForth does (in colorForth
the editor is formally a part of the language).

-Billy

stevan apter — 2006-07-26 21:23:24

----- Original Message -----
From: "William Tanksley, Jr" <wtanksleyjr@...>
To: <concatenative@yahoogroups.com>
Sent: Tuesday, July 25, 2006 2:51 PM
Subject: Re: [stack] Re: 'stack' and 'unstack' in F


> stevan apter <sa@...> wrote:
> > From: "William Tanksley, Jr" <wtanksleyjr@...>
> > > I could be wrong :-). I think my dislike for dip-like solutions is
> > > betraying me, honestly. (dip provides you with a second stack, but
> > > doesn't let you manage it, thereby making it impossible to screw it up
> > > -- a good thing and a sometimes inconvenient thing both at the same
> > > time.)
>
> > i've never used (or implemented) a return stack in any of my toy
> > languages, and in fact i'm not entirely clear on how it works in
> > forth. in F, 'dip' swaps the first two items on the stack and
> > pushes them onto the queue, where first the program will evaluate,
> > and then the element which was formerly on top of the stack will
> > evaluate.
>
> Forth's return stack is vaguely like your queue, except that the
> return stack doesn't hold anything from the currently executing word.
> That information is maintained in the IP (instruction pointer).
> Because Forth doesn't include any currently executing information on
> the return stack, manipulation of the return stack won't affect the
> current word's execution, only the execution of words after the
> current word returns.
>
> > a pattern i find myself using repeatedly is a combination of 'dip',
> > 'pair' (to shorten the stack), 'bot' and 'top' (i.e. 'rot' and '-rot'),
> > and 'unpair' (to unpack paired elements.) it's just too painful to
> > try to work with more than four or five stack elements.
>
> Forth doesn't have list manipulations, but if it did most Forthers
> would discourage their use for the purpose of stack manipulations. The
> #1 best way to not suffer the pain of dealing with large numbers of
> stack elements is to design so that you don't need large numbers of
> stack elements :-);

yes, this is the key to working productively in these stack languages.
for some time i've been aware of the very different criteria for naming
code fragments used by programmers working in concatenative and applicative
languages. in an applicative language (at least, the ones i'm familiar with)
there's an implicit rule that you don't abstract to a named subfunction
unless (i) you're going to invoke it from more than one superfunction, and
(ii) the subfunction "merits" a name by "expressing a well-formed thought."
(quotes indicate that these are vague, but meaningful ideas, of the "i don't
know how to define it, but i recognize one when i see it" variety.) in
concatenative languages, these rules are simply too difficult to apply, or
so it seems to me. moreover, you're like to get lots more named fragments
than you will in an applicative language.

after that, use the return stack/queue. I
> personally would prefer the stack manipulation sublanguage (I've
> mentioned it to you before) to using the return stack; it's much more
> self-documenting, and usually faster.

you're referring to shuffle-notation? i've added a version of that
to F, but it's probably too strong for your tastes. :) here's the
relevant section of the document:

Patterns
F incorporates a version of the pattern sublanguage of XY.

A pattern is a list whose head is a scheme and whose tail is a template

A scheme is a name or a list of schemes. The case of the first letter in a name is significant. If lower-case, the name matches a
single element. If upper-case, the name must occur as the final element of a list of schemes, and will match zero or more elements,
viz. the remainder of the list whose initial elements are matched by schemes preceding the terminal name.

A template is a list. If s is a symbol in the template which also occurs in the scheme, the value matched by s is substituted for s
in the template.

F has two operations involving patterns. Both are implemented as commands, rather than primitives.

"S" applies a pattern to the stack and pushes the result onto the stack.

"Q" applies a pattern to the stack and prepends the result to the queue.

Examples:

F>10 20 [[a b]b a] "S"
20 10

F>[2 + 3] [[a b c]a c b] "Q"
5

F>[2 + 3] [[a b c]a c b] "S"
2 3 +

F>10 [20 30 40 50][[a [b c D]]D c b a] "S"
[40 50] 30 20 10

stevan apter — 2006-07-26 21:29:23

>
> F>[2 + 3] [[a b c]a c b] "Q"

that's wrong. should be:

F>[2 + 3] [[[a b c]]a c b] "Q"

> 5
>
> F>[2 + 3] [[a b c]a c b] "S"

ditto:

F>[2 + 3] [[[a b c]]a c b] "S"

> 2 3 +
>
> F>10 [20 30 40 50][[a [b c D]]D c b a] "S"
> [40 50] 30 20 10
>

William Tanksley, Jr — 2006-07-27 03:47:42

stevan apter <sa@...> wrote:
> From: "William Tanksley, Jr" <wtanksleyjr@...>
> > Forth doesn't have list manipulations, but if it did most Forthers
> > would discourage their use for the purpose of stack manipulations. The
> > #1 best way to not suffer the pain of dealing with large numbers of
> > stack elements is to design so that you don't need large numbers of
> > stack elements :-);

> yes, this is the key to working productively in these stack languages.

Agreed.

I think it's worth noting that it's also a general rule of thumb for
writing efficient code -- don't have your functions consume vast
amounts of disorganized, dynamic data at once, or require deep digging
for data. So it's not just "busywork" to have to clean up your code to
be nice for a concatenative language; in general, it's a good thing
for your code. (There are, of course, exceptions to this rule.)

> for some time i've been aware of the very different criteria for naming
> code fragments used by programmers working in concatenative and applicative
> languages. in an applicative language (at least, the ones i'm familiar with)
> there's an implicit rule that you don't abstract to a named subfunction
> unless (i) you're going to invoke it from more than one superfunction, and
> (ii) the subfunction "merits" a name by "expressing a well-formed thought."
> (quotes indicate that these are vague, but meaningful ideas, of the "i don't
> know how to define it, but i recognize one when i see it" variety.) in
> concatenative languages, these rules are simply too difficult to apply, or
> so it seems to me.

I don't see why rule (i) should be hard to apply; in fact, it can be
easier to find common code, because of the lack of named parameters
makes similar code look similar (similar to the property called
"referential transparency"), and it's possible to "factor out" common
code simply by cutting and pasting (whereas you need a special tool to
extract a method when there are named parameters mixed in with the
code).

Rule (ii) is applicable to both types of languages, though; and I
agree that it's not easy in either.

> moreover, you're like to get lots more named fragments
> than you will in an applicative language.

This is true. One point I'd consider is that stack-based languages
have a very low call overhead, so it's cheap to factor code out. (The
overhead is low because applicative languages, in general, have to
copy every parameter every time any function's called. The only solid
way around that is whole-program optimization.)

> > after that, use the return stack/queue. I
> > personally would prefer the stack manipulation sublanguage (I've
> > mentioned it to you before) to using the return stack; it's much more
> > self-documenting, and usually faster.

> you're referring to shuffle-notation? i've added a version of that
> to F, but it's probably too strong for your tastes. :) here's the
> relevant section of the document:

Grin. You know my taste well. I'm a connoisseur of fine whines.

> Patterns
> F incorporates a version of the pattern sublanguage of XY.
> A pattern is a list whose head is a scheme and whose tail is a template

(Clipped.)

Looks nice to me. Like you said, a little rich, but not without a
pleasant aftertaste.

-Billy

stevan apter — 2006-07-27 11:19:49

>
> > moreover, you're like to get lots more named fragments
> > than you will in an applicative language.
>
> This is true. One point I'd consider is that stack-based languages
> have a very low call overhead, so it's cheap to factor code out. (The
> overhead is low because applicative languages, in general, have to
> copy every parameter every time any function's called. The only solid
> way around that is whole-program optimization.)

this isn't true. e.g. k doesn't copy on call. it creates a pointer
and bumps the reference count.

anyway, the reason named fragments proliferate at a faster rate
in cl's is that you spend more code moving things into position
on the stack in order to do meaningful work, and factoring
will apply to both types of code. for example, the code to
take X Y Z W to W Z X Y so that 'foo' can evaluate properly
is, from the point of view of factoring, equivalent to 'foo'
itself, whereas in an applicative language, you just call 'foo'
with 'foo[W;Z;X;Y]'. W, X, Y, and Z aren't ordered until you
lock them into place in a call.

William Tanksley, Jr — 2006-07-27 13:29:21

stevan apter <sa@...> wrote:
> > > moreover, you're like to get lots more named fragments
> > > than you will in an applicative language.
> > This is true. One point I'd consider is that stack-based languages
> > have a very low call overhead, so it's cheap to factor code out. (The
> > overhead is low because applicative languages, in general, have to
> > copy every parameter every time any function's called. The only solid
> > way around that is whole-program optimization.)

> this isn't true. e.g. k doesn't copy on call. it creates a pointer
> and bumps the reference count.

It's still true -- you still have to build the stack frame and copy
*something* into it. You actually have to do a little more than I
mentioned, since you additionally have to access a reference counter
in memory.

> anyway, the reason named fragments proliferate at a faster rate
> in cl's is that you spend more code moving things into position
> on the stack in order to do meaningful work, and factoring
> will apply to both types of code. for example, the code to
> take X Y Z W to W Z X Y so that 'foo' can evaluate properly
> is, from the point of view of factoring, equivalent to 'foo'
> itself, whereas in an applicative language, you just call 'foo'
> with 'foo[W;Z;X;Y]'. W, X, Y, and Z aren't ordered until you
> lock them into place in a call.

That's certainly another reason why named fragments would proliferate,
yes. (For all the good and bad that your point implies!) I will add
that most people don't write _many_ stack shufflings as definitions of
their own -- it happens, but not commonly. Perhaps a reason for that
is that if you perform a stack manipulation twice, it's a clue that
you need to rethink your stack order.

This is IMO a great reason to have a simple, clear stack manipulation
lexer, with a syntax designed so that each stack manipulator can be
read as a gestalt and so that stack manipulations aren't easy to
confuse with other code. This is one problem I've had with many of the
stack shuffle parsers people have suggested -- they look like
quotations, or they look like strings.

My suggestion is to build a stack op compiler into the language's lexer.

-Billy

sa@dfa.com — 2006-07-27 13:58:39

concatenative@yahoogroups.com wrote on 07/27/2006 09:29:21 AM:

> stevan apter <sa@...> wrote:
> > > > moreover, you're like to get lots more named fragments
> > > > than you will in an applicative language.
> > > This is true. One point I'd consider is that stack-based languages
> > > have a very low call overhead, so it's cheap to factor code out. (The
> > > overhead is low because applicative languages, in general, have to
> > > copy every parameter every time any function's called. The only solid
> > > way around that is whole-program optimization.)
>
> > this isn't true. e.g. k doesn't copy on call. it creates a pointer
> > and bumps the reference count.
>
> It's still true -- you still have to build the stack frame and copy
> *something* into it. You actually have to do a little more than I
> mentioned, since you additionally have to access a reference counter
> in memory.
>
> > anyway, the reason named fragments proliferate at a faster rate
> > in cl's is that you spend more code moving things into position
> > on the stack in order to do meaningful work, and factoring
> > will apply to both types of code. for example, the code to
> > take X Y Z W to W Z X Y so that 'foo' can evaluate properly
> > is, from the point of view of factoring, equivalent to 'foo'
> > itself, whereas in an applicative language, you just call 'foo'
> > with 'foo[W;Z;X;Y]'. W, X, Y, and Z aren't ordered until you
> > lock them into place in a call.
>
> That's certainly another reason why named fragments would proliferate,
> yes. (For all the good and bad that your point implies!) I will add
> that most people don't write _many_ stack shufflings as definitions of
> their own -- it happens, but not commonly. Perhaps a reason for that
> is that if you perform a stack manipulation twice, it's a clue that
> you need to rethink your stack order.
>
> This is IMO a great reason to have a simple, clear stack manipulation
> lexer, with a syntax designed so that each stack manipulator can be
> read as a gestalt and so that stack manipulations aren't easy to
> confuse with other code. This is one problem I've had with many of the
> stack shuffle parsers people have suggested -- they look like
> quotations, or they look like strings.
>
> My suggestion is to build a stack op compiler into the language's lexer.

so e.g. you would want something like

ab--ba ...

where x--y means "match x, reorder to y"?

>
> -Billy
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>

William Tanksley, Jr — 2006-07-28 01:27:32

sa@... <sa@...> wrote:
> so e.g. you would want something like
> ab--ba ...
> where x--y means "match x, reorder to y"?

Exactly. So ab--ba is "swap".

I do like your pattern language's power (truly!), but I find its
spaciness a little less readable.

-Billy

stevan apter — 2006-07-28 16:16:43

i like the leaner notation as well. in practice, you'll
be using shuffles more often than patterns. so i've
implemented the simpler form as a (hitherto illegal)
symbol-form, viz.

F>10 20 ab:ba
20 10

with shuffles and patterns, several operators become
redundant, viz. pop, dup, swap, cons, and uncons. a
significant fork, so i've produced a version of F
with the primitives remapped (and the pattern operators
integrated into the language itself).

F is still here:

http://www.nsl.com/k/f/f.htm

and the variant, F_, here:

http://www.nsl.com/k/f_/f.htm

shuffles are atomic, and this has consequences for
expressivity. so e.g.

J>10 20 30 40 swap pop swap dup
10 40 20 20
J>

J>10 20 30 40 abcd:adbb
10 40 20 20
J>

we can do things with the quotation

[swap pop swap dup]

we can't do with the shuffle

abcd:adbb

viz. program-transformation.

----- Original Message -----
From: "William Tanksley, Jr" <wtanksleyjr@...>
To: <concatenative@yahoogroups.com>
Sent: Thursday, July 27, 2006 9:27 PM
Subject: Re: [stack] Re: 'stack' and 'unstack' in F


> sa@... <sa@...> wrote:
> > so e.g. you would want something like
> > ab--ba ...
> > where x--y means "match x, reorder to y"?
>
> Exactly. So ab--ba is "swap".
>
> I do like your pattern language's power (truly!), but I find its
> spaciness a little less readable.
>
> -Billy
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>

stevan apter — 2006-07-28 16:49:34

and of course we can do things with patterns we
can't do with either.

a pattern is a quotation which is a diagram of
the stack or queue in the 'before' state and
the stack or queue in the 'after' state. so we
can write pattern-combinators which transform
patterns into patterns. but we can't write
shuffle-combinators, and we can't decode the
before and after states of a general quotation.

----- Original Message -----
From: "stevan apter" <sa@...>
To: <concatenative@yahoogroups.com>
Sent: Friday, July 28, 2006 12:16 PM
Subject: Re: [stack] Re: 'stack' and 'unstack' in F


> i like the leaner notation as well. in practice, you'll
> be using shuffles more often than patterns. so i've
> implemented the simpler form as a (hitherto illegal)
> symbol-form, viz.
>
> F>10 20 ab:ba
> 20 10
>
> with shuffles and patterns, several operators become
> redundant, viz. pop, dup, swap, cons, and uncons. a
> significant fork, so i've produced a version of F
> with the primitives remapped (and the pattern operators
> integrated into the language itself).
>
> F is still here:
>
> http://www.nsl.com/k/f/f.htm
>
> and the variant, F_, here:
>
> http://www.nsl.com/k/f_/f.htm
>
> shuffles are atomic, and this has consequences for
> expressivity. so e.g.
>
> J>10 20 30 40 swap pop swap dup
> 10 40 20 20
> J>
>
> J>10 20 30 40 abcd:adbb
> 10 40 20 20
> J>
>
> we can do things with the quotation
>
> [swap pop swap dup]
>
> we can't do with the shuffle
>
> abcd:adbb
>
> viz. program-transformation.
>
> ----- Original Message -----
> From: "William Tanksley, Jr" <wtanksleyjr@...>
> To: <concatenative@yahoogroups.com>
> Sent: Thursday, July 27, 2006 9:27 PM
> Subject: Re: [stack] Re: 'stack' and 'unstack' in F
>
>
> > sa@... <sa@...> wrote:
> > > so e.g. you would want something like
> > > ab--ba ...
> > > where x--y means "match x, reorder to y"?
> >
> > Exactly. So ab--ba is "swap".
> >
> > I do like your pattern language's power (truly!), but I find its
> > spaciness a little less readable.
> >
> > -Billy
> >
> >
> >
> > Yahoo! Groups Links
> >
> >
> >
> >
> >
> >
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>

sa@dfa.com — 2006-07-31 12:58:17

from the F doc:

Patterns and Shuffles

F incorporates a version of the pattern sublanguage of XY.

A pattern is a list whose head is a scheme and whose tail is a template

A scheme is a name or a list of schemes. The case of the first letter in a
name is significant. If lower-case, the name matches a single element. If
upper-case, the name must occur as the final element in a list of schemes,
and will match zero or more elements, viz. the remainder of the list whose
initial elements are matched by schemes preceding the terminal name.

A template is a list. If s is a symbol in the template which also occurs in
the scheme, the value matched by s is substituted for s in the template.

F contains four primitive operations on patterns.

) applies a pattern to the stack and pushes the result onto the
stack.

( applies a pattern to the stack and prepends the result to the
queue.

} applies a pattern to the queue and pushes the result onto the
stack.

{ applies a pattern to the queue and prepends the result to the
queue.

As an example of how patterns can simplify programming in F, compare the
definition of cond as given in the prelude:

[pair![dupd!]dip!quote!!dip!swap!false!pair!index!!] cond

with an equivalent definition using the stack-to-pattern operator (:

[[[d i t f]d[[t f]]d i!false!join!index!!](] pcond

pcond expects a data-item d, a condition i of d, a program t to evaluate on
d if d i is true (i.e. not false), and a program f to evaluate on d if d i
is false.

The template evaluates the condition and uses the result to index out the
appropriate program, which is then evaluated on the data-item.

The pattern version is arguably clearer than the version which makes use of
stack operators. One objection to the use of patterns is that the resulting
programs violate the Concatenativity property: a concatenative program can
be cut into two sub-programs, both of which are concatenative programs, as
long as the cut does not fall within the boundaries of a quotation. For
example, cond can be cut into:

[pair![dupd!]dip!quote!!dip!] acond
[swap!false!pair!index!!] bcond

and then cond can be defined as:

[acond!bcond!] cond

But pcond cannot be cut into smaller programs, because every pattern-based
program has the structure [pattern pattern-operator] and patterns are
quotations. In other words, the meaning of symbols in the pattern-template
depends on the assignments implied by the pattern-scheme.

F also supports shuffle-symbols, a simplified, weaker version of (. For
example, swap is:

F>10 20 ab-ba
20 10

Shuffles can reduce stack-noise, but the resulting programs still have the
Concatenativity property. For example, cond can be defined as:

[ditf-dditf pair!unit!quote!!dip!swap!false!join!index!!] scond

concatenative@yahoogroups.com wrote on 07/27/2006 09:27:32 PM:

> sa@... <sa@...> wrote:
> > so e.g. you would want something like
> > ab--ba ...
> > where x--y means "match x, reorder to y"?
>
> Exactly. So ab--ba is "swap".
>
> I do like your pattern language's power (truly!), but I find its
> spaciness a little less readable.
>
> -Billy
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>