Tuples in Factor
Slava Pestov — 2005-01-30 20:28:27
Hi,
I have implemented true user-defined types in Factor, called 'tuples'.
You can read details at
http://jroller.com/page/slava/.
Tuples are integrated with the generic word system, and the built-in
support for delegation makes a lot of design patterns trivial to implement.
Slava
phimvt@lurac.latrobe.edu.au — 2005-01-31 07:32:41
Dear concatenators,
Here is a problem that had me stumped for a while. It illustrates
a recurring problem in concatenative languages: complex stack
manipulations.
An equation of the form a * x^2 + b * x + c = 0
has either no solution in the reals, or it has two solutions
in the reals ("not necessarily distinct" as they say).
The solutions or roots, if any, are given by
-b +/- sqrt(b^2 - 4 * a * c)
-----------------------------
2 * a
This is known as the quadratic formula. A google search for
" quadratic formula XYZ language"
for XYZ = almost any language under the sun will find good and bad
programs in that language. But such a program is annoyingly difficult
to write in a concatenative language. Such a program should expect
three parameters (a b c) on the stack, and return either the two roots
separately on the stack or, if the language allows it, a single
list or vector consisting of the two roots. What makes the problem
so annoying is the fact that two of the parameters (a & b) occur twice
for each of the two roots, and also the fact that the computations
for the two roots are almost identical, except that one has + where
the other has -.
To give an example, for a = 2, b = -4, c = -6, with the -6 on top
of the stack, the program should give the two roots 3 and -1.
I have a program in Joy for this, and I shall write a little web page
about this and similar problems. But I invite other concatenative
programmers to try their own luck in their favourite concatenative
language. If your language allows assignments, as Forth does, try
to do without them.
Don't get too annoyed. I cannot pay your psychiatric bills.
Enjoy, unannoyed.
- Manfred
Ivan Tomac — 2005-01-31 11:35:51
Here's what I ended up with. Without comments it's 5 lines not
including the helper functions '+-' and 'over'.
Granted the functions could have been better named...
LIBRA
(* -b +/- sqrt(b^2 - 4 * a * c)
----------------------------
2 * a
term1 = b^2 - 4 * a * c
term2 = -b
term3 = 2 * a
*)
over == dupd swap;
(* a b -- a+b a-b *)
+- == over over - [+] dip;
(* a b c -- a a b c b *)
setup == [dupd] dip over;
(* a b c -- term1 *)
term1 == [dup * swap] dip 4.0 * * - sqrt;
(* a term1 b -- b+term1 b-term1 2a *)
resolve == [swap 2.0 *] dip swap [swap +-] dip;
(* a b c -- r1 r2 *)
quad == setup [term1] dip resolve
swap over / [/] dip.
2.0 -4.0 -6.0 quad put put.
--- In concatenative@yahoogroups.com, phimvt@l... wrote:
>
> Dear concatenators,
>
> Here is a problem that had me stumped for a while. It illustrates
> a recurring problem in concatenative languages: complex stack
> manipulations.
>
> An equation of the form a * x^2 + b * x + c = 0
> has either no solution in the reals, or it has two solutions
> in the reals ("not necessarily distinct" as they say).
> The solutions or roots, if any, are given by
>
> -b +/- sqrt(b^2 - 4 * a * c)
> -----------------------------
> 2 * a
>
> This is known as the quadratic formula. A google search for
> " quadratic formula XYZ language"
> for XYZ = almost any language under the sun will find good and bad
> programs in that language. But such a program is annoyingly
difficult
> to write in a concatenative language. Such a program should expect
> three parameters (a b c) on the stack, and return either the two
roots
> separately on the stack or, if the language allows it, a single
> list or vector consisting of the two roots. What makes the problem
> so annoying is the fact that two of the parameters (a & b) occur
twice
> for each of the two roots, and also the fact that the computations
> for the two roots are almost identical, except that one has + where
> the other has -.
>
> To give an example, for a = 2, b = -4, c = -6, with the -6 on top
> of the stack, the program should give the two roots 3 and -1.
>
> I have a program in Joy for this, and I shall write a little web
page
> about this and similar problems. But I invite other concatenative
> programmers to try their own luck in their favourite concatenative
> language. If your language allows assignments, as Forth does, try
> to do without them.
>
> Don't get too annoyed. I cannot pay your psychiatric bills.
> Enjoy, unannoyed.
>
> - Manfred
Ivan Tomac — 2005-01-31 11:40:47
As usual Yahoo decided to mess up my indenting. Anyway I forgot to
negate b so the 'resolve' function should be:
resolve == [swap 2.0 *] dip swap [neg swap +-] dip;
--- In concatenative@yahoogroups.com, "Ivan Tomac" <e1_t@y...> wrote:
>
> Here's what I ended up with. Without comments it's 5 lines not
> including the helper functions '+-' and 'over'.
> Granted the functions could have been better named...
>
> LIBRA
>
> (* -b +/- sqrt(b^2 - 4 * a * c)
> ----------------------------
> 2 * a
>
> term1 = b^2 - 4 * a * c
> term2 = -b
> term3 = 2 * a
> *)
>
> over == dupd swap;
>
> (* a b -- a+b a-b *)
> +- == over over - [+] dip;
>
> (* a b c -- a a b c b *)
> setup == [dupd] dip over;
>
> (* a b c -- term1 *)
> term1 == [dup * swap] dip 4.0 * * - sqrt;
>
> (* a term1 b -- b+term1 b-term1 2a *)
> resolve == [swap 2.0 *] dip swap [swap +-] dip;
>
> (* a b c -- r1 r2 *)
> quad == setup [term1] dip resolve
> swap over / [/] dip.
>
> 2.0 -4.0 -6.0 quad put put.
>
> --- In concatenative@yahoogroups.com, phimvt@l... wrote:
> >
> > Dear concatenators,
> >
> > Here is a problem that had me stumped for a while. It illustrates
> > a recurring problem in concatenative languages: complex stack
> > manipulations.
> >
> > An equation of the form a * x^2 + b * x + c = 0
> > has either no solution in the reals, or it has two solutions
> > in the reals ("not necessarily distinct" as they say).
> > The solutions or roots, if any, are given by
> >
> > -b +/- sqrt(b^2 - 4 * a * c)
> > -----------------------------
> > 2 * a
> >
> > This is known as the quadratic formula. A google search for
> > " quadratic formula XYZ language"
> > for XYZ = almost any language under the sun will find good and bad
> > programs in that language. But such a program is annoyingly
> difficult
> > to write in a concatenative language. Such a program should expect
> > three parameters (a b c) on the stack, and return either the two
> roots
> > separately on the stack or, if the language allows it, a single
> > list or vector consisting of the two roots. What makes the problem
> > so annoying is the fact that two of the parameters (a & b) occur
> twice
> > for each of the two roots, and also the fact that the computations
> > for the two roots are almost identical, except that one has + where
> > the other has -.
> >
> > To give an example, for a = 2, b = -4, c = -6, with the -6 on top
> > of the stack, the program should give the two roots 3 and -1.
> >
> > I have a program in Joy for this, and I shall write a little web
> page
> > about this and similar problems. But I invite other concatenative
> > programmers to try their own luck in their favourite concatenative
> > language. If your language allows assignments, as Forth does, try
> > to do without them.
> >
> > Don't get too annoyed. I cannot pay your psychiatric bills.
> > Enjoy, unannoyed.
> >
> > - Manfred
Slava Pestov — 2005-01-31 12:56:50
Here is the Factor solution. Its a bit messy, but it will find both real
and complex roots.
IN: quadratic USING: kernel math ;
: quadratic-e 2 * / neg ;
: quadratic-d pick 4 * * swap sq swap - swap sq 4 * / sqrt ;
: quadratic-roots 2dup + -rot - ;
: quadratic ( a b c -- alpha beta )
#! Finds both roots of the polynomial a*x^2 + b*x + c
#! using the quadratic formula.
3dup quadratic-d
nip swap rot quadratic-e
swap quadratic-roots ;
phimvt@... wrote:
>
> Dear concatenators,
>
> Here is a problem that had me stumped for a while. It illustrates
> a recurring problem in concatenative languages: complex stack
> manipulations.
>
> An equation of the form a * x^2 + b * x + c = 0
> has either no solution in the reals, or it has two solutions
> in the reals ("not necessarily distinct" as they say).
> The solutions or roots, if any, are given by
>
> -b +/- sqrt(b^2 - 4 * a * c)
> -----------------------------
> 2 * a
>
> This is known as the quadratic formula. A google search for
> " quadratic formula XYZ language"
> for XYZ = almost any language under the sun will find good and bad
> programs in that language. But such a program is annoyingly difficult
> to write in a concatenative language. Such a program should expect
> three parameters (a b c) on the stack, and return either the two roots
> separately on the stack or, if the language allows it, a single
> list or vector consisting of the two roots. What makes the problem
> so annoying is the fact that two of the parameters (a & b) occur twice
> for each of the two roots, and also the fact that the computations
> for the two roots are almost identical, except that one has + where
> the other has -.
>
> To give an example, for a = 2, b = -4, c = -6, with the -6 on top
> of the stack, the program should give the two roots 3 and -1.
>
> I have a program in Joy for this, and I shall write a little web page
> about this and similar problems. But I invite other concatenative
> programmers to try their own luck in their favourite concatenative
> language. If your language allows assignments, as Forth does, try
> to do without them.
>
> Don't get too annoyed. I cannot pay your psychiatric bills.
> Enjoy, unannoyed.
>
> - Manfred
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
>
janvanlent — 2005-01-31 20:00:56
--- In concatenative@yahoogroups.com, phimvt@l... wrote:
<snip>
> An equation of the form a * x^2 + b * x + c = 0
> has either no solution in the reals, or it has two solutions
> in the reals ("not necessarily distinct" as they say).
<snip>
> programs in that language. But such a program is annoyingly difficult
> to write in a concatenative language. Such a program should expect
> three parameters (a b c) on the stack, and return either the two roots
> separately on the stack or, if the language allows it, a single
> list or vector consisting of the two roots.
Three solutions in forth (gforth,
http://www.jwdt.com/~paysan/gforth.html).
The first solution illustrates that the straightforward implementation
is not numerically stable. Only the first few significant digits of
the smallest solution to problem t2 are correct.
jan
\ ***
\ calculate solution to quadratic equation
\ $a x + b x^2 + c = 0$
\ test problem (solutions -1 and 3)
: t1
2e0 -4e0 -6e0 ;
\ test problem
\ (from a course by Dirk Laurie based on the book
\ http://www.ec-securehost.com/SIAM/ot86.html)
: t2
1e0 -1e5 208341e0 66317e0 f/ 312689e0 99532e0 f/ f- fdup 1e4 f- f* ;
\ ***
\ using assignment
\ (this version is not numerically stable)
marker quad1
create qa float allot : qa@ qa f@ ; : qa! qa f! ;
create qb float allot : qb@ qb f@ ; : qb! qb f! ;
create qc float allot : qc@ qc f@ ; : qc! qc f! ;
create qd float allot : qd@ qd f@ ; : qd! qd f! ;
: disc frot f* -4e0 f* fswap fdup f* f+ ;
: q ( a b c -- ... )
qc! qb! qa!
qa@ qb@ qc@ disc qd!
qd@ f0< if 0 else 2
qb@ fnegate qb! qa@ f2* qa! qd@ fsqrt qd!
qb@ qd@ f+ qa@ f/
qb@ qd@ f- qa@ f/ then ;
t1 q . f. f. cr
t2 q . fs. fs. cr
quad1
\ ***
\ using stack manipulation
marker quad2
: f2dup ( r1 r2 -- r1 r2 r1 r2 )
fover fover ;
\ floating point sign, never returns zero
: fsign ( r1 -- r2 )
f0< if -1e0 else 1e0 then ;
\ discriminant of equation $x^2 - 2 b x + c = 0$
: disc ( c b -- D )
fdup f* fswap f- ;
\ no real solutions
: q0 ( c b D -- )
fdrop fdrop fdrop ;
\ two real solutions
: q2 ( c b D -- x1 x2 )
fsqrt fover fsign f* f+ ftuck f/ ;
\ solution of equation $x^2 - 2 b x + c = 0$
: q' ( c b -- [ x1 x2 ] n )
f2dup disc fdup f0< if q0 0 else q2 2 then ;
\ normalize equation $x^2 + b x + c = 0$ to $x^2 - 2 b x + c = 0$
: qnorm ( b c a -- c' b' )
ftuck f/ frot frot f/ -0.5e0 f* ;
\ solve linear equation $b x + c = 0$
: lin ( b c -- x )
fnegate fswap f/ ;
\ solve quadratic equation $x^2 + b x + c = 0$
: q ( a b c -- [ x1 [ x2 ] ] n )
frot fdup f0= if fdrop lin 1 else qnorm q' then ;
t1 q . f. f. cr
t2 q . fs. fs. cr
quad2
\ ***
\ using gforth locals
marker quad3
: fsign ( r1 -- r2 )
f0< if -1e0 else 1e0 then ;
: disc { F: b F: c -- D }
b b f* c f- ;
: q' { F: b F: c -- [ x1 x2 ] n }
b c disc
fdup f0< if fdrop 0 else fsqrt b fsign f* b f+ fdup c fswap f/ 2 then ;
: qnorm { F: a F: b F: c -- b' c' }
b a f/ -0.5e0 f* c a f/ ;
: q { F: a F: b F: c -- [ x1 [ x2 ] ] n }
a f0= if c fnegate b f/ 1 else a b c qnorm q' then ;
t1 q . f. f. cr
t2 q . fs. fs. cr
quad3
------
$ gforth quad.fs -e bye
2 -1. 3.
2 1.45519152283669E-11 1.00000000000000E5
2 -1. 3.
2 1.51499701672722E-11 1.00000000000000E5
2 -1. 3.
2 1.51499701672722E-11 1.00000000000000E5
sa@dfa.com — 2005-01-31 20:31:38
phimvt@... wrote on 01/31/2005 02:32:41 AM:
>
>
> Dear concatenators,
>
> Here is a problem that had me stumped for a while. It illustrates
> a recurring problem in concatenative languages: complex stack
> manipulations.
>
> An equation of the form a * x^2 + b * x + c = 0
> has either no solution in the reals, or it has two solutions
> in the reals ("not necessarily distinct" as they say).
> The solutions or roots, if any, are given by
>
> -b +/- sqrt(b^2 - 4 * a * c)
> -----------------------------
> 2 * a
>
> This is known as the quadratic formula. A google search for
> " quadratic formula XYZ language"
> for XYZ = almost any language under the sun will find good and bad
> programs in that language. But such a program is annoyingly difficult
> to write in a concatenative language. Such a program should expect
> three parameters (a b c) on the stack, and return either the two roots
> separately on the stack or, if the language allows it, a single
> list or vector consisting of the two roots. What makes the problem
> so annoying is the fact that two of the parameters (a & b) occur twice
> for each of the two roots, and also the fact that the computations
> for the two roots are almost identical, except that one has + where
> the other has -.
>
> To give an example, for a = 2, b = -4, c = -6, with the -6 on top
> of the stack, the program should give the two roots 3 and -1.
>
> I have a program in Joy for this, and I shall write a little web page
> about this and similar problems. But I invite other concatenative
> programmers to try their own luck in their favourite concatenative
> language. If your language allows assignments, as Forth does, try
> to do without them.
i can do without assignments, and XY's pseudo-argument pattern-matching
takes care of the stack-diddling:
; q1 { [a b c] b -: a c * 4 * b 2 ^ -. _sqrt + a 2 * % } ;
; q2 { [a b c] b -: a c * 4 * b 2 ^ -. _sqrt - a 2 * % } ;
there are two ways to factor out the +/-:
pass the operator as an argument o:
; f_ { [o [a b c]] b -: a c * 4 * b 2 ^ -. _sqrt o i a 2 * % } ;
; f { [a b c] [+ -] [a b c] [f_] each\ } ;
2 -4 -6 f
[3.0 -1.0]
or use an old APL trick:
2+3*1 -1
5 -1
i.e. simulate x +/- y by adding scalar x to vector [y -y]:
; g { [a b c] b -: a c * 4 * b 2 ^ -. _sqrt [1 -1] * + a 2 * % } ;
2 -4 -6 g
[3.0 -1.0]
q1/q2 and f are atomic: apply them to vectors and get vectors back:
[2 3 4][-4 -5 -6][-6 -7 -8] q1
[3.0 2.573384 2.350781]
[2 3 4][-4 -5 -6][-6 -7 -8] q2
[-1.0 -0.9067178 -0.8507811]
>
> Don't get too annoyed. I cannot pay your psychiatric bills.
but will you pay my beer-bill?
> Enjoy, unannoyed.
>
> - Manfred
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
John Carter — 2005-02-04 06:00:58
On Mon, 31 Jan 2005
phimvt@... wrote:
> Don't get too annoyed. I cannot pay your psychiatric bills.
Fascinating exercise.
The XY implementation essentially used a Lispish Lambda to temporarily
bind parts of the stack to names.
The Joy and Factor implementations essentially did a "compile time Lambda"
permanently binding a bunch of "helper" expressions to names.
The Quadratic Formula is not annoying, its just name binding is a handy
short hand notation.
If you don't use it, you either replace it with "compile time Lambda" or
have longer expressions. Those are the breaks.
Question: Is this an absolute problem of concatenative languages, or
merely a product of the lack of manauvering room on a 1D linear stack.
Read
http://www.geom.uiuc.edu/~banchoff/Flatland/
to understand what I mean by the limitations of 1D.
Could one envision a 2D or 3D or N-D concatenative language?
Perhaps, I'm wildly supposing using maximum of 30 seconds of thought
here...
Suppose a 2D stack is a stack of stacks. A program is a
list of lists, where each sublist is applied simultaneously to the
corresponding stack.
It all needs more thought than I have time to expend now.
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email :
john.carter@...
New Zealand
"The notes I handle no better than many pianists. But the pauses
between the notes -
ah, that is where the art resides!' - Artur Schnabel
Ivan Tomac — 2005-02-05 08:59:49
--- In
concatenative@yahoogroups.com, John Carter <john.carter@t...>
wrote:
> Question: Is this an absolute problem of concatenative languages,
or
> merely a product of the lack of manauvering room on a 1D linear
stack.
>
*snip*
> Suppose a 2D stack is a stack of stacks. A program is a
> list of lists, where each sublist is applied simultaneously to the
> corresponding stack.
>
This inspired me to implement the thread combinator and rewrite the
implementation of the quadratic function in Joy:
(*----------------------------------*)
"seqlib.joy" include.
LIBRA
(* -b +/- sqrt(b^2 - 4 * a * c)
----------------------------
2 * a
term1 = sqrt(b^2 - 4 * a * c)
term2 = -b
term3 = 2 * a
*)
(* a b -- a b a *)
over == dupd swap;
(* a b -- a+b a-b *)
+- == over over - [+] dip;
(* a n -- [a a a...] *)
replicate == [] swap [dupd cons] times popd;
(* [a1 a2...] n -- [b1 b2...] *)
thread == swap dup [size [[] swap [cons] times reverse] dip replicate]
dip zip [i infra reverse] map;
(* ------------------------------------------------------ *)
(* a b c -- r1 r2 *)
quad == [[pop popd neg] (* term2 *)
[[dup * swap] dip 4.0 * * - sqrt] (* term1 *)
[pop pop 2.0 *] (* term3 *)] 3 thread
flatten i
[+-] dip swap over / [/] dip.
2.0 -4.0 -6.0 quad put put.
(* ------------------------------------------------------- *)
thread takes 2 parameters. The first parameter, a, is a list of
functions. The second one is number of elements, n, on the stack that
each function will be applied to. In other words thread pushes n
elements from the stack into a new stack and each of the functions in
a is then applied to that stack. The results are then compiled into a
list of stacks. For example:
1 2 3 [[dup] [4 swap]] 2 thread --> 1 [[2 3 3] [2 4 3]]
It's not quite what John was talking about though.
Ivan
P.S. Sorry about indenting... I have no idea how to get Yahoo mail to
not remove spaces...
John Carter — 2005-02-06 20:51:58
On Sat, 5 Feb 2005, Ivan Tomac wrote:
> It's not quite what John was talking about though.
Well, given that John (me) wasn't sure what he (I) was talking about
anyway... I would call that a very good go.
I'm pretty sure that any 2D or N-D arrangement of stacks will be found
to be equivalent / emulatable with a 1D stack in Joy.
What I'm hoping is some of the bookkeeping work that name binding
relieves us of could be done in a concatenative manner by going to 2D
stacks. Cellular automata and that amazing proof that Conway's Game of
Life can emulate a Turing machine comes to mind. (However I'm hoping
for something _much_ simpler.)
I'm a bit out of practice with Joy programming at the moment, otherwise I
would do this myself...
Add an operator (I'm sure you can define it in Joy), that
takes a list of lists of Joy operators.
a b c [[program1] [program2] [program3]] new_threads
whose effect would be to convert that into running in lock step, 3 programs simultaneously,
a program1
b program2
c program3
The result being
[a program1] [b program2] [c program3]
And here is the cunning bit that makes it different from the usual...
Since each program operates step by step with it's neighbour, also add
operators dup_left (and dup_right) that dup whatever is on top
of the right(left) hand stack and push it onto the current stack.
I'm sure other arrangements can be imagined, some might even be useful...
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email :
john.carter@...
New Zealand
"The notes I handle no better than many pianists. But the pauses
between the notes -
ah, that is where the art resides!' - Artur Schnabel
Slava Pestov — 2005-02-06 21:22:43
I've been watching this thread with interest, but I think you guys are
taking the wrong approach. Instead of asking "how can I make a complex
stack effect manageable?" you should be asking "how can I _simplify_ the
stack effect?" Indeed, not everything has to be kept on the stack...
John Carter wrote:
> On Sat, 5 Feb 2005, Ivan Tomac wrote:
>
>
>>It's not quite what John was talking about though.
>
>
> Well, given that John (me) wasn't sure what he (I) was talking about
> anyway... I would call that a very good go.
>
> I'm pretty sure that any 2D or N-D arrangement of stacks will be found
> to be equivalent / emulatable with a 1D stack in Joy.
>
> What I'm hoping is some of the bookkeeping work that name binding
> relieves us of could be done in a concatenative manner by going to 2D
> stacks. Cellular automata and that amazing proof that Conway's Game of
> Life can emulate a Turing machine comes to mind. (However I'm hoping
> for something _much_ simpler.)
>
> I'm a bit out of practice with Joy programming at the moment, otherwise I
> would do this myself...
>
> Add an operator (I'm sure you can define it in Joy), that
> takes a list of lists of Joy operators.
>
> a b c [[program1] [program2] [program3]] new_threads
>
> whose effect would be to convert that into running in lock step, 3 programs simultaneously,
>
> a program1
> b program2
> c program3
>
> The result being
> [a program1] [b program2] [c program3]
>
> And here is the cunning bit that makes it different from the usual...
>
> Since each program operates step by step with it's neighbour, also add
> operators dup_left (and dup_right) that dup whatever is on top
> of the right(left) hand stack and push it onto the current stack.
>
> I'm sure other arrangements can be imagined, some might even be useful...
>
>
>
>
> John Carter Phone : (64)(3) 358 6639
> Tait Electronics Fax : (64)(3) 359 4632
> PO Box 1645 Christchurch Email : john.carter@...
> New Zealand
>
> "The notes I handle no better than many pianists. But the pauses
> between the notes -
> ah, that is where the art resides!' - Artur Schnabel
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
>
John Carter — 2005-02-06 22:03:08
On Sun, 6 Feb 2005, Slava Pestov wrote:
> I've been watching this thread with interest, but I think you guys are
> taking the wrong approach. Instead of asking "how can I make a complex
> stack effect manageable?" you should be asking "how can I _simplify_ the
> stack effect?" Indeed, not everything has to be kept on the stack...
The reason why I'm going through these contortions is to try somehow
keep the nifty concatenative property that makes rewriting Joy such an,
umm, Joy, and lose some of the endless dup swap over stack fiddling.
So that leaves precisely two places to store stuff, (as far as I can
see)...
* in compile time names. ie. The '==' operator, which has the downside of
polluting the namespace with trivia. (Which given the simplicity of
Joy, might not be such a Bad Thing. A decent "library" facility
for Joy would also search for and suggest other similar program
fragments already in the Library, and automagically rewrite all
client fragments if you should wish to replace an existing fragment
by something more generic..)
* the stack.
I'm very interested in where else we may keep information and retain the
concatenative property.
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email :
john.carter@...
New Zealand
"The notes I handle no better than many pianists. But the pauses
between the notes -
ah, that is where the art resides!' - Artur Schnabel
stevan apter — 2005-02-06 22:21:57
From: "Slava Pestov" <
slava@...>
>
> I've been watching this thread with interest, but I think you guys are
> taking the wrong approach. Instead of asking "how can I make a complex
> stack effect manageable?" you should be asking "how can I _simplify_ the
> stack effect?" Indeed, not everything has to be kept on the stack...
me too, but i've drawn a different conclusion. the computation,
written in "math", is clear and easy to understand, but without
the ability to name and reuse parts of the stack, the resulting
concatenative programs are obscure and hard to grasp. the math
is a single line, and unless the code is as well, it will be
difficult to persuade people of the virtues of the concatenative
approach.
names are good. and they don't violate what billy has called "the
concatenativity test":
"a language is concatenative if any two valid programs can be
concatenated to form a valid program, and any valid program can
be split at any lexical boundary to form two valid programs."
depending on what "valid" means, one might have to adjust the
meaning of "lexical boundary". alternatively, given a pattern
and a program, such as
[a b c]
b -: a c * 4 * b 2 ^ -. .5 ^ [1 -1] * + a 2 * %
eliminate names by transforming the code into "pure" concatenative
code, viz. code in which the names are replaced by the implied stack
manipulations. i've been meaning to write this for a while.
>
> John Carter wrote:
> > On Sat, 5 Feb 2005, Ivan Tomac wrote:
> >
> >
> >>It's not quite what John was talking about though.
> >
> >
> > Well, given that John (me) wasn't sure what he (I) was talking about
> > anyway... I would call that a very good go.
> >
> > I'm pretty sure that any 2D or N-D arrangement of stacks will be found
> > to be equivalent / emulatable with a 1D stack in Joy.
> >
> > What I'm hoping is some of the bookkeeping work that name binding
> > relieves us of could be done in a concatenative manner by going to 2D
> > stacks. Cellular automata and that amazing proof that Conway's Game of
> > Life can emulate a Turing machine comes to mind. (However I'm hoping
> > for something _much_ simpler.)
> >
> > I'm a bit out of practice with Joy programming at the moment, otherwise I
> > would do this myself...
> >
> > Add an operator (I'm sure you can define it in Joy), that
> > takes a list of lists of Joy operators.
> >
> > a b c [[program1] [program2] [program3]] new_threads
> >
> > whose effect would be to convert that into running in lock step, 3 programs simultaneously,
> >
> > a program1
> > b program2
> > c program3
> >
> > The result being
> > [a program1] [b program2] [c program3]
> >
> > And here is the cunning bit that makes it different from the usual...
> >
> > Since each program operates step by step with it's neighbour, also add
> > operators dup_left (and dup_right) that dup whatever is on top
> > of the right(left) hand stack and push it onto the current stack.
> >
> > I'm sure other arrangements can be imagined, some might even be useful...
> >
> >
> >
> >
> > John Carter Phone : (64)(3) 358 6639
> > Tait Electronics Fax : (64)(3) 359 4632
> > PO Box 1645 Christchurch Email : john.carter@...
> > New Zealand
> >
> > "The notes I handle no better than many pianists. But the pauses
> > between the notes -
> > ah, that is where the art resides!' - Artur Schnabel
> >
> >
> >
> >
> > Yahoo! Groups Links
> >
> >
> >
> >
> >
> >
> >
> >
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
Slava Pestov — 2005-02-06 22:19:31
John Carter wrote:
> The reason why I'm going through these contortions is to try somehow
> keep the nifty concatenative property that makes rewriting Joy such an,
> umm, Joy, and lose some of the endless dup swap over stack fiddling.
What about having objects on the stack, where an object contains several
named fields, as in Factor?
Slava
Slava Pestov — 2005-02-06 22:22:21
In that case, if you're dealing with extremely complicated formulas, why
not just embed a domain-specific language that parses infix? Most code
does not contain complicated formulas.
stevan apter wrote:
> From: "Slava Pestov" <slava@...>
>
>>I've been watching this thread with interest, but I think you guys are
>>taking the wrong approach. Instead of asking "how can I make a complex
>>stack effect manageable?" you should be asking "how can I _simplify_ the
>>stack effect?" Indeed, not everything has to be kept on the stack...
>
>
> me too, but i've drawn a different conclusion. the computation,
> written in "math", is clear and easy to understand, but without
> the ability to name and reuse parts of the stack, the resulting
> concatenative programs are obscure and hard to grasp. the math
> is a single line, and unless the code is as well, it will be
> difficult to persuade people of the virtues of the concatenative
> approach.
>
> names are good. and they don't violate what billy has called "the
> concatenativity test":
>
> "a language is concatenative if any two valid programs can be
> concatenated to form a valid program, and any valid program can
> be split at any lexical boundary to form two valid programs."
>
> depending on what "valid" means, one might have to adjust the
> meaning of "lexical boundary". alternatively, given a pattern
> and a program, such as
>
> [a b c]
> b -: a c * 4 * b 2 ^ -. .5 ^ [1 -1] * + a 2 * %
>
> eliminate names by transforming the code into "pure" concatenative
> code, viz. code in which the names are replaced by the implied stack
> manipulations. i've been meaning to write this for a while.
stevan apter — 2005-02-06 22:24:57
From: "Slava Pestov" <
slava@...>
>
> John Carter wrote:
> > The reason why I'm going through these contortions is to try somehow
> > keep the nifty concatenative property that makes rewriting Joy such an,
> > umm, Joy, and lose some of the endless dup swap over stack fiddling.
>
> What about having objects on the stack, where an object contains several
> named fields, as in Factor?
in my opinion, too much setup is required. the quadratic
algorithm takes three numbers, not an object containing
three name-number pairs.
>
> Slava
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
stevan apter — 2005-02-06 22:29:38
From: "Slava Pestov" <
slava@...>
>
> In that case, if you're dealing with extremely complicated formulas, why
> not just embed a domain-specific language that parses infix? Most code
> does not contain complicated formulas.
the quadratic formula isn't extremely complicated.
besides, it's not the infix/postfix difference which is causing
the problem (or problems).
problem 1 is the fact that, without names, the amount of code
required to diddle the stack overwhelms the relatively trivial
arithmetic involved. looking at the code, you just can't *see*
the math; the math isn't *salient*.
problem 2 is the fact that the +/- is hard to factor in a
convincing, transparent way. personally, i find the "APL trick"
very satisfying, but that requires an underlying array semantics.
>
> stevan apter wrote:
> > From: "Slava Pestov" <slava@...>
> >
> >>I've been watching this thread with interest, but I think you guys are
> >>taking the wrong approach. Instead of asking "how can I make a complex
> >>stack effect manageable?" you should be asking "how can I _simplify_ the
> >>stack effect?" Indeed, not everything has to be kept on the stack...
> >
> >
> > me too, but i've drawn a different conclusion. the computation,
> > written in "math", is clear and easy to understand, but without
> > the ability to name and reuse parts of the stack, the resulting
> > concatenative programs are obscure and hard to grasp. the math
> > is a single line, and unless the code is as well, it will be
> > difficult to persuade people of the virtues of the concatenative
> > approach.
> >
> > names are good. and they don't violate what billy has called "the
> > concatenativity test":
> >
> > "a language is concatenative if any two valid programs can be
> > concatenated to form a valid program, and any valid program can
> > be split at any lexical boundary to form two valid programs."
> >
> > depending on what "valid" means, one might have to adjust the
> > meaning of "lexical boundary". alternatively, given a pattern
> > and a program, such as
> >
> > [a b c]
> > b -: a c * 4 * b 2 ^ -. .5 ^ [1 -1] * + a 2 * %
> >
> > eliminate names by transforming the code into "pure" concatenative
> > code, viz. code in which the names are replaced by the implied stack
> > manipulations. i've been meaning to write this for a while.
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
Slava Pestov — 2005-02-06 22:29:39
stevan apter wrote:
> in my opinion, too much setup is required. the quadratic
> algorithm takes three numbers, not an object containing
> three name-number pairs.
I wasn't suggesting it for the quadratic algorithm, just in general.
BTW Why don't we see you in #concatenative anymore?
Slava
Martin Young — 2005-02-06 22:29:27
I agree.
My feeling is that if the convatenative approach ever takes off then
it will be as a "bottom layer" language which is suitable for
analysis, optimisation and other (semi)automatic optimisation. The
actual programming will be done by higher level languages and
tools. The higher level tools may even be lambda detived languages.
I'm doing a little work in this direction. Anybody else?
Regards,
Martin.
--- In concatenative@yahoogroups.com, "stevan apter" <sa@n...> wrote:
> me too, but i've drawn a different conclusion. the computation,
> written in "math", is clear and easy to understand, but without
> the ability to name and reuse parts of the stack, the resulting
> concatenative programs are obscure and hard to grasp. the math
> is a single line, and unless the code is as well, it will be
> difficult to persuade people of the virtues of the concatenative
> approach.
stevan apter — 2005-02-06 22:37:31
From: "Slava Pestov" <
slava@...>
>
> stevan apter wrote:
> > in my opinion, too much setup is required. the quadratic
> > algorithm takes three numbers, not an object containing
> > three name-number pairs.
>
> I wasn't suggesting it for the quadratic algorithm, just in general.
ok.
>
> BTW Why don't we see you in #concatenative anymore?
just taking a holiday. i'll be back.
>
> Slava
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
Slava Pestov — 2005-02-06 22:43:19
Martin Young wrote:
>
> I agree.
>
> My feeling is that if the convatenative approach ever takes off then
> it will be as a "bottom layer" language which is suitable for
> analysis, optimisation and other (semi)automatic optimisation. The
> actual programming will be done by higher level languages and
> tools. The higher level tools may even be lambda detived languages.
I think the concatenative approach works well for many problems. The
only difficulty I've found is with mathematical formulas; everything
else seems to break down into object oriented and functional
abstractions that are expressed nicely in concatenative code.
Slava
John Carter — 2005-02-07 01:15:39
On Sun, 6 Feb 2005, Slava Pestov wrote:
> John Carter wrote:
>> The reason why I'm going through these contortions is to try somehow
>> keep the nifty concatenative property that makes rewriting Joy such an,
>> umm, Joy, and lose some of the endless dup swap over stack fiddling.
>
> What about having objects on the stack, where an object contains several
> named fields, as in Factor?
Please excuse my perversity, I have been reading XSLT manuals all day
so my brain is sore...
> Here is the Factor solution. Its a bit messy, but it will find both real
> and complex roots.
> IN: quadratic USING: kernel math ;
> : quadratic-e 2 * / neg ;
> : quadratic-d pick 4 * * swap sq swap - swap sq 4 * / sqrt ;
> : quadratic-roots 2dup + -rot - ;
> : quadratic ( a b c -- alpha beta )
> #! Finds both roots of the polynomial a*x^2 + b*x + c
> #! using the quadratic formula.
> 3dup quadratic-d
> nip swap rot quadratic-e
> swap quadratic-roots ;
I read that as what I call "compile time lambda" creating a bunch of
small helper functions that correspond to subexpressions.
ie. not named fields of an object, and the 3dup, nip, swap, rot, swap is the
stack fiddling glue that glues them together.
It is quite likely I am missing something about how Factor works.
(Also I would like to see an example of how named fields and the
"string concatenation of arbitrary program segments === function
composition" works together.
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email :
john.carter@...
New Zealand
"The notes I handle no better than many pianists. But the pauses
between the notes -
ah, that is where the art resides!' - Artur Schnabel
John Carter — 2005-02-07 01:17:30
On Sun, 6 Feb 2005, Martin Young wrote:
> My feeling is that if the convatenative approach ever takes off then
> it will be as a "bottom layer" language which is suitable for
> analysis, optimisation and other (semi)automatic optimisation. The
> actual programming will be done by higher level languages and
> tools. The higher level tools may even be lambda detived languages.
Provided it is a reversible transformation from the high level to low
level and back again.
That Algebra on Joy is just too sweet to lose.
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email :
john.carter@...
New Zealand
"The notes I handle no better than many pianists. But the pauses
between the notes -
ah, that is where the art resides!' - Artur Schnabel
Slava Pestov — 2005-02-07 01:22:40
John Carter wrote:
>>IN: quadratic USING: kernel math ;
>
>
>>: quadratic-e 2 * / neg ;
>>: quadratic-d pick 4 * * swap sq swap - swap sq 4 * / sqrt ;
>>: quadratic-roots 2dup + -rot - ;
>
>
>>: quadratic ( a b c -- alpha beta )
>> #! Finds both roots of the polynomial a*x^2 + b*x + c
>> #! using the quadratic formula.
>> 3dup quadratic-d
>> nip swap rot quadratic-e
>> swap quadratic-roots ;
>
>
>
> I read that as what I call "compile time lambda" creating a bunch of
> small helper functions that correspond to subexpressions.
>
> ie. not named fields of an object, and the 3dup, nip, swap, rot, swap is the
> stack fiddling glue that glues them together.
This particular example does not use objects on the stack. I was just
pointing out that a general way to reduce stack noise is to group values
inside objects.
Slava
John Cowan — 2005-02-07 02:30:45
Martin Young scripsit:
> My feeling is that if the convatenative approach ever takes off then
> it will be as a "bottom layer" language which is suitable for
> analysis, optimisation and other (semi)automatic optimisation.
Already true, I think: assembly language is concatenative, with the
store and registers taking the place of the stack.
--
John Cowan
jcowan@... www.reutershealth.com www.ccil.org/~cowan
I come from under the hill, and under the hills and over the hills my paths
led. And through the air. I am he that walks unseen. I am the clue-finder,
the web-cutter, the stinging fly. I was chosen for the lucky number. --Bilbo
John Carter — 2005-02-07 03:13:47
On Sun, 6 Feb 2005, Slava Pestov wrote:
> This particular example does not use objects on the stack. I was just
> pointing out that a general way to reduce stack noise is to group values
> inside objects.
First a context Question, which came first, the Factor developer's
guide or Manfred's annoying Quadratic question?
I see the Factor developer guide uses the Quadratic as an
example. ie. Have you just been working in Manfred's question into
your developer guide as we have been speaking, or did Manfred get the
question from reading your manual, or was it coincidence?
I went looking in there to try understand how you were doing named
fields, I assume via your namespace facility?
I also hoped such a facility would make Joy friendlier, until Manfred
pointed out that such things destroyed the concatenative property and
hence the algebra.
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email :
john.carter@...
New Zealand
"The notes I handle no better than many pianists. But the pauses
between the notes -
ah, that is where the art resides!' - Artur Schnabel
Slava Pestov — 2005-02-07 03:33:17
John Carter wrote:
> First a context Question, which came first, the Factor developer's
> guide or Manfred's annoying Quadratic question?
The Factor developer's guide has sadly not been updated for quite a while ;)
> I went looking in there to try understand how you were doing named
> fields, I assume via your namespace facility?
Namespaces are one way; a more recent development is tuples, which I
documented at
http://www.jroller.com/page/slava/20050130#tuples_in_factor_delegation_vs
> I also hoped such a facility would make Joy friendlier, until Manfred
> pointed out that such things destroyed the concatenative property and
> hence the algebra.
Tuples do not destroy either property if you stick to immutable tuples.
I myself am more concerned with practicality rather than theoretical
purity, so I regularly use object oriented techniques, such as objects
with mutable state.
Slava
John Carter — 2005-02-07 05:50:40
On Sun, 6 Feb 2005, Slava Pestov wrote:
>
> John Carter wrote:
>> First a context Question, which came first, the Factor developer's
>> guide or Manfred's annoying Quadratic question?
>
> The Factor developer's guide has sadly not been updated for quite a while ;)
Ah! So I assume Manfred got the vexing question from you then.
>> I also hoped such a facility would make Joy friendlier, until Manfred
>> pointed out that such things destroyed the concatenative property and
>> hence the algebra.
>
> Tuples do not destroy either property if you stick to immutable tuples.
> I myself am more concerned with practicality rather than theoretical
> purity, so I regularly use object oriented techniques, such as objects
> with mutable state.
So am I far more concerned with practicality, but it is that purity that
makes easy rewriting possible, and its that easy rewriting that is of such
enormous practical importance.
Hmm. Let me see if I understand your tuples (the version of Factor I
have just installed chokes on the first line, so I wonder if tuples are
too new a feature?)...
TUPLE: point3d a b c ;
C: point3d ( a b c -- point3d ) [ set-point-c ] keep [ set-point-b ] keep [set-point-a] keep ;
: quadratic (a b c -- point) <point3d>
#! Store point 3d for next op
dup
#! Compute the -b
point3d-b :-
#! Move the -b out the way and bring up the point3d again
swap dup
#! Compute bit under sqrt (point3d -- real)
dup point3d-b dup * swap dup point3d-a swap point3d-c 4 * * - sqrt
#! Hide the sqrt part and bring up the point 3d again
swap
#! Compute the 2a (point3d -- real)
point3d-a 2 *
#! At this stage I have -b delta 2a on the stack
<point3d> #! Name it all again.
dup #! Store it so we have it around for the -ive root
# Compute +ive root
dup point3d-a swap dup point3d-b + swap point3d-c /
swap #! Bring up our partial result point3d
dup point3d-a swap dup point3d-b - swap point3d-c /
#! We have +ive root and -ive root on stack, make a point out of it.
<point> ;
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email :
john.carter@...
New Zealand
"The notes I handle no better than many pianists. But the pauses
between the notes -
ah, that is where the art resides!' - Artur Schnabel
Ivan Tomac — 2005-02-07 08:12:16
--- In
concatenative@yahoogroups.com, John Carter <john.carter@t...>
wrote:
> On Sat, 5 Feb 2005, Ivan Tomac wrote:
>
> > It's not quite what John was talking about though.
>
> Well, given that John (me) wasn't sure what he (I) was talking about
Yes.. sorry about that, I forgot whom I was replying to so it came out
a bit weird - I didn't even notice until now.
>
> Add an operator (I'm sure you can define it in Joy), that
> takes a list of lists of Joy operators.
>
> a b c [[program1] [program2] [program3]] new_threads
>
> whose effect would be to convert that into running in lock step, 3
programs simultaneously,
>
> a program1
> b program2
> c program3
>
> The result being
> [a program1] [b program2] [c program3]
>
> And here is the cunning bit that makes it different from the
usual...
>
> Since each program operates step by step with it's neighbour, also
add
> operators dup_left (and dup_right) that dup whatever is on top
> of the right(left) hand stack and push it onto the current stack.
>
> I'm sure other arrangements can be imagined, some might even be
useful...
>
Very interesting idea. But how would you know what would dup_left or
dup_right come up with on the stack? Wouldn't that require all the top
level functions in each program to execute at the same speed? Also
what happens to functions called from within the top level functions:
are they also allowed to call dup_left as well?
Or maybe I misunderstood what you meant.
Side-effects would also be another problem with code that runs in
parallel although that's the case with my version of threads as well
if it was in fact implemented to execute all the programs
simultaneously.
Ivan
William Tanksley, Jr — 2005-02-07 15:57:30
John Carter <
john.carter@...> wrote:
> The reason why I'm going through these contortions is to try somehow
> keep the nifty concatenative property that makes rewriting Joy such an,
> umm, Joy, and lose some of the endless dup swap over stack fiddling.
I agree. I'd even more like to figure out how to express the
properties of some of the extensions to the concatenative languages
we've been looking at -- extensions like the return stack
manipulations, continuations, source manipulations, and so on. I'd
like to come up with something as simple and elegant as
concatenativity for them.
I'm thinking that some of them might allow for more expressive
algebraic manipulations within a concatenative language.
> So that leaves precisely two places to store stuff, (as far as I can
> see)...
> * in compile time names. ie. The '==' operator, which has the downside of
> polluting the namespace with trivia. (Which given the simplicity of
> Joy, might not be such a Bad Thing. A decent "library" facility
> for Joy would also search for and suggest other similar program
> fragments already in the Library, and automagically rewrite all
> client fragments if you should wish to replace an existing fragment
> by something more generic..)
I don't like local variables (nor, of course, globals). They don't
preserve the concatenative property, and don't seem to add any
interesting new property; their only advantage is that they're what
people trained in lambda-based languages naturally expect. I suspect
them of being a cop-out that's hiding a much better solution from us.
> * the stack.
Arrays/lists on the stack. This is especially apt for mathematically
derived formulas, in which variable names like 'a' 'b' and 'c' aren't
really important or meaningful.
> I'm very interested in where else we may keep information and retain the
> concatenative property.
There may also be other properties we can keep, or new properties we
may be able to discover that aren't present in purely concatenative
languages.
> John Carter Phone : (64)(3) 358 6639
-Billy
sa@dfa.com — 2005-02-07 17:36:50
"William Tanksley, Jr" <
wtanksleyjr@...> wrote on 02/07/2005 10:57:30
AM:
>
> John Carter <john.carter@...> wrote:
> > The reason why I'm going through these contortions is to try somehow
> > keep the nifty concatenative property that makes rewriting Joy such an,
> > umm, Joy, and lose some of the endless dup swap over stack fiddling.
>
> I agree. I'd even more like to figure out how to express the
> properties of some of the extensions to the concatenative languages
> we've been looking at -- extensions like the return stack
> manipulations, continuations, source manipulations, and so on. I'd
> like to come up with something as simple and elegant as
> concatenativity for them.
>
> I'm thinking that some of them might allow for more expressive
> algebraic manipulations within a concatenative language.
>
> > So that leaves precisely two places to store stuff, (as far as I can
> > see)...
> > * in compile time names. ie. The '==' operator, which has the
downside of
> > polluting the namespace with trivia. (Which given the simplicity of
> > Joy, might not be such a Bad Thing. A decent "library" facility
> > for Joy would also search for and suggest other similar program
> > fragments already in the Library, and automagically rewrite all
> > client fragments if you should wish to replace an existing fragment
> > by something more generic..)
>
> I don't like local variables (nor, of course, globals). They don't
> preserve the concatenative property, and don't seem to add any
> interesting new property; their only advantage is that they're what
> people trained in lambda-based languages naturally expect. I suspect
> them of being a cop-out that's hiding a much better solution from us.
i'm not sure i understand what you mean by "local variable". the
ability to associate a name with a position on the stack, and then
use that name to denote the value at that position, seems to me
to be the most natural and direct way of reducing stack noise in
the code.
so, issues of habit and familiarity to one side, can you provide
an argument showing names (in this weak sense) are incompabible
with concatenativity, or restrict existing algebraic transformations?
there is a third possibility, but it takes us completely out of
the world of linear, text-based programming. i'm thinking of the
graphical notation developed by the philosopher charles peirce,
in which formulas using quantifiers and variables are rendered
as two dimensional arrangements of predicate and function symbols
whose argument positions are connected by "lines of identity."
for example
ExEy Fxy & Gy
is rendered as
F . .------+
| |
G .
information on the graphs at these sites:
http://www.jfsowa.com/peirce/ms514.htm
http://www.dr-dau.net/eg_readings.shtml
http://www.existentialgraphs.com/
http://www.clas.ufl.edu/users/jzeman/
>
> > * the stack.
>
> Arrays/lists on the stack. This is especially apt for mathematically
> derived formulas, in which variable names like 'a' 'b' and 'c' aren't
> really important or meaningful.
>
> > I'm very interested in where else we may keep information and retain
the
> > concatenative property.
>
> There may also be other properties we can keep, or new properties we
> may be able to discover that aren't present in purely concatenative
> languages.
>
> > John Carter Phone : (64)(3) 358 6639
>
> -Billy
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
John Carter — 2005-02-07 20:03:54
On Mon, 7 Feb 2005, Ivan Tomac wrote:
>> And here is the cunning bit that makes it different from the
> usual...
>>
>> Since each program operates step by step with it's neighbour, also
> add
>> operators dup_left (and dup_right) that dup whatever is on top
>> of the right(left) hand stack and push it onto the current stack.
>>
>> I'm sure other arrangements can be imagined, some might even be
> useful...
>>
>
> Very interesting idea. But how would you know what would dup_left or
> dup_right come up with on the stack? Wouldn't that require all the top
> level functions in each program to execute at the same speed?
Not really, it just means you have to execute one operator from each
program in each time step, and you have to cache what is on top of all
stacks before you start executing the next step.
> Also
> what happens to functions called from within the top level functions:
> are they also allowed to call dup_left as well?
One operator per step would be the rule.
> Side-effects would also be another problem with code that runs in
> parallel although that's the case with my version of threads as well
> if it was in fact implemented to execute all the programs
> simultaneously.
I think if we shorten that paragraph to
"Side-effect be a problem"
and we have it in a nut shell.
My favourite answer is, "Well don't do that then."
So let's try this new_thread idea...
Instead of dup_left and dup_right, I'm going to use dup_up
and dup_down.
Consider three stacks
a
b
c
Lets on the first on build the 2a, on the second build the sqrt(b^2-4ac),
and the last, build the -b
a 2 *
b dup_up nop dup_down 4 * * swap dup *
c dup_up swap drop :-
a b c [[2 *][dup_up nop dup_down 4 * * swap dup *] [dup_up swap drop :-]] new_threads
Resulting in...
2a delta -b
So make two stacks this time....
delta .... Here we are going to build the -ive root
-b .... And here the +ive
delta dup_down swap -
-b dup_up +
So that part of the program would be...(delta -b -- [2a* -iveroot, 2a* +iveroot])
delta -b [[dup_down swap -] [dup_up +]] new_threads
So to divide by 2a
2a [-ive*2a, +ive*2a] swap [*] swons map
Putting it all together....
quadratic (*a b c -- [-ive, +ive]*) ==
[[2 *][dup_up nop dup_down 4 * * swap dup *] [dup_up swap drop :-]] new_threads
[[dup_down swap -] [dup_up +]] new_threads
[-ive*2a, +ive*2a] swap [*] swons map;
I quite like that.
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email :
john.carter@...
New Zealand
"The notes I handle no better than many pianists. But the pauses
between the notes -
ah, that is where the art resides!' - Artur Schnabel
Nick Forde — 2005-02-07 20:14:34
On 7 Feb 2005, at 17:36,
sa@... wrote:
> "William Tanksley, Jr" <wtanksleyjr@...> wrote on 02/07/2005
> 10:57:30AM:
>> I don't like local variables (nor, of course, globals). They don't
>> preserve the concatenative property, and don't seem to add any
>> interesting new property; their only advantage is that they're what
>> people trained in lambda-based languages naturally expect. I suspect
>> them of being a cop-out that's hiding a much better solution from us.
>
> i'm not sure i understand what you mean by "local variable". the
> ability to associate a name with a position on the stack, and then
> use that name to denote the value at that position, seems to me
> to be the most natural and direct way of reducing stack noise in
> the code.
I agree. I can't think of any situation where an immutable stack
reference couldn't
be replaced by a series of equivalent stack operators.
As I said in an old thread on this list I think being able to choose the
most appropriate solution gives the best of both worlds and I think it
can
lead to clearer code.
(see
http://groups.yahoo.com/group/concatenative/message/1786 for an
example)
Currently I find programming in concatenative languages a bit like
mining for diamonds.
Most of the time is spent covering rough ground but every so often I
stumble across a
gem that reminds me that the rewards can be great. I have faith that
Billy is right and
there is a better solution out there somewhere. But maybe that's just a
greedy prospectors
blind optimism :-)
Regards,
Nick.
John Carter — 2005-02-07 20:26:32
On Mon, 7 Feb 2005, William Tanksley, Jr wrote:
>
> John Carter <john.carter@...> wrote:
>> The reason why I'm going through these contortions is to try somehow
>> keep the nifty concatenative property that makes rewriting Joy such an,
>> umm, Joy, and lose some of the endless dup swap over stack fiddling.
>
> I agree. I'd even more like to figure out how to express the
> properties of some of the extensions to the concatenative languages
> we've been looking at -- extensions like the return stack
> manipulations, continuations, source manipulations, and so on. I'd
> like to come up with something as simple and elegant as
> concatenativity for them.
I'm trying hard to figure out whether what Manfred has done is something
deep, profound and fundamental about the universe.... Or whether it is a
mere curious side-effect of the particular technology I'm using. (Strings
of char's on a PC screen.) My grouchy cynic nature keeps muttering its a
trick, a mere sleight of hand. But part of me keeps eying those Algebra
for Joy and rewriting Joy and Joy0 pages and going "Wow!"
I keep saying it in my head, over and over, "Functional composition in a
Concatenative language is....X"
Where the first suggestion for X is "string concatenation", not quite
true, since if A and B are fragments of Joy, then A whitespace B not AB is
the functional composition of B(A()).
Thus perhaps X is "List concat".
"Function Composition in a concatenative language is List Concatenation"
On the other I can equally write...
"Functional composition in Lispish languages is List nesting".
ie. What we are talking about is in a sense, merely a coincidence between
the base data-structures of the language and a fundamental mathematical
operation.
Admittedly the more such coincidences you can arrange/design, and the more
fundamental the mathematical operations are, the more fun is your
language.
There's a Language Design Principle for you, "Arrange coincidences between
basic operations on your primitive data types and high level mathematical
operations."
> I don't like local variables (nor, of course, globals). They don't
> preserve the concatenative property, and don't seem to add any
> interesting new property; their only advantage is that they're what
> people trained in lambda-based languages naturally expect. I suspect
> them of being a cop-out that's hiding a much better solution from us.
An important property of lambda based languages is that _every_
call to a new word is essentially wrapped in an "infra", limiting the
(potential) damage to the stack.
infra : L1 [P] -> L2
Using list L1 as stack, executes P and returns a new list L2. The
first element of L1 is used as the top of stack, and after execution of P
the top of stack becomes the first element of L2.
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email :
john.carter@...
New Zealand
"The notes I handle no better than many pianists. But the pauses
between the notes -
ah, that is where the art resides!' - Artur Schnabel
sa@dfa.com — 2005-02-07 20:44:37
John Carter <
john.carter@...> wrote on 02/07/2005 03:26:32 PM:
>
> ie. What we are talking about is in a sense, merely a coincidence between
> the base data-structures of the language and a fundamental mathematical
> operation.
>
> Admittedly the more such coincidences you can arrange/design, and the
more
> fundamental the mathematical operations are, the more fun is your
> language.
>
> There's a Language Design Principle for you, "Arrange coincidences
between
> basic operations on your primitive data types and high level mathematical
> operations."
>
and arrange these coincidences in order to maximize the number of useful
operations on combinations involving the fewest number of keystrokes.
i can't resist injecting an anecdote from the earliest days of (what else?)
the k language.
working on an implementation of SQL, i needed a function to perform multi-
field sort on a table; i.e., in t, sort f up, g down, h up. arthur whitney
had the office next to mine, so i strolled over and posed the problem.
after 30 seconds of silence, he walked over to the whiteboard and wrote:
sort:{x y z x}/[_n]
that is, given table t, field-list f, and a list of f-many sort functions
o,
i:sort[o;t f]
returns i, the permutation of t into sorted order.
a few hours later, i needed a function to perform table selection; i.e.,
the equivalent of SQL's "where" clause. so once again i strolled into
arthur's office and posed the problem. after another 30 seconds of
silence,
he walked over to the whiteboard, erased "sort", and replaced it with
"select":
select:{x y z x}/[_n]
that is, given table t, field list f, and a list of f-many boolean
selection
functions o
i:select[o;t f]
returns i, the indices of the records of t which satisfy the conjunction of
the selection criteria o applied to each f.
John Carter — 2005-02-07 21:23:38
On Mon, 7 Feb 2005
sa@... wrote:
> John Carter <john.carter@...> wrote on 02/07/2005 03:26:32 PM:
>>
>> ie. What we are talking about is in a sense, merely a coincidence
>> between the base data-structures of the language and a fundamental
>> mathematical operation.
>>
>> Admittedly the more such coincidences you can arrange/design, and
>> the more fundamental the mathematical operations are, the more fun
>> is your language.
>>
>> There's a Language Design Principle for you, "Arrange coincidences
>> between basic operations on your primitive data types and high
>> level mathematical operations."
> and arrange these coincidences in order to maximize the number of useful
> operations on combinations involving the fewest number of keystrokes.
Ah, that's the bit I object to. Minimizing keystrokes. I feel then I'm
giving into "Optimizing for the current technology" rather than "There
is something profound going on here".
If all concatenative languages are merely a trick to "Minimize
Keystrokes", then I'm not convinced of their long term worth.
However, I believe there is something more here, I suspect your statement should read,
"If program fragments in your language are represented by primitive data type Y,
operations on Y should satisfy the same axioms as program
fragments. ie, Program fragments and data type Y should both be
instances of the same abstract mathematical object."
> i can't resist injecting an anecdote from the earliest days of (what else?)
> the k language.
>
> working on an implementation of SQL, i needed a function to perform multi-
> field sort on a table; i.e., in t, sort f up, g down, h up. arthur whitney
> had the office next to mine, so i strolled over and posed the problem.
> after 30 seconds of silence, he walked over to the whiteboard and wrote:
>
> sort:{x y z x}/[_n]
Ahh, that 30 seconds silence.
So few people are capable of that.
From a human perspective that must be a profound weakness of K.
Most humans cannot make that thirty seconds of silence. They have to
fill it up with some jabber, some doodling and fiddling that brings
them closer to the answer.
To silently sit in ambush on truth is not the human way.
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email :
john.carter@...
New Zealand
"The notes I handle no better than many pianists. But the pauses
between the notes -
ah, that is where the art resides!' - Artur Schnabel
John Carter — 2005-02-07 21:59:51
On Tue, 8 Feb 2005, John Carter wrote:
> Ahh, that 30 seconds silence.
>
> So few people are capable of that.
In case the tone got lost in translation to ASCII text format, I was
joking and certainly not referring to anybody present.
Well mostly joking. A certain hard core of truth, perhaps, but mostly
joking.
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email :
john.carter@...
New Zealand
"The notes I handle no better than many pianists. But the pauses
between the notes -
ah, that is where the art resides!' - Artur Schnabel
John Cowan — 2005-02-07 22:27:13
John Carter scripsit:
>
> On Tue, 8 Feb 2005, John Carter wrote:
>
> > Ahh, that 30 seconds silence.
> >
> > So few people are capable of that.
>
> In case the tone got lost in translation to ASCII text format, I was
> joking and certainly not referring to anybody present.
I read it like this:
> > Ahh, that 30 seconds silence.
> > [30 second silence]
> > So few people are capable of that.
--
We call nothing profound
jcowan@...
that is not wittily expressed. John Cowan
--Northrop Frye (improved)
http://www.reutershealth.com
John Carter — 2005-02-07 22:35:11
On Mon, 7 Feb 2005, John Cowan wrote:
> I read it like this:
>
>>> Ahh, that 30 seconds silence.
>>> [30 second silence]
>>> So few people are capable of that.
For once, the lossy, noisy, unreliable communication channel that is email,
improved the message in transit.
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email :
john.carter@...
New Zealand
"The notes I handle no better than many pianists. But the pauses
between the notes -
ah, that is where the art resides!' - Artur Schnabel
stevan apter — 2005-02-08 00:35:38
From: "John Carter" <
john.carter@...>
>
> > and arrange these coincidences in order to maximize the number of useful
> > operations on combinations involving the fewest number of keystrokes.
>
> Ah, that's the bit I object to. Minimizing keystrokes. I feel then I'm
> giving into "Optimizing for the current technology" rather than "There
> is something profound going on here".
is that how you interpreted my sort/select anecdote?
what i meant to suggest was that, other things being equal,
the less clutter in the language, the more likely it is that
the notation will reveal unexpected and interesting identities.
for example, that multifield sort = converging boolean selection.
once you've seen that this is so -- once you have, so to speak,
a constructive proof of the assertion, in the form of a piece
of code which computes both functions -- than you can ask of
a given language P, can i write one piece of P-code which
computes both functions? and if not, why not?
i certainly wasn't thinking at all about the quite different
problem of "optimizing for current technology".
John Carter — 2005-02-08 02:15:50
On Mon, 7 Feb 2005, stevan apter wrote:
> From: "John Carter" <john.carter@...>
>>
>>> and arrange these coincidences in order to maximize the number of useful
>>> operations on combinations involving the fewest number of keystrokes.
>>
>> Ah, that's the bit I object to. Minimizing keystrokes. I feel then I'm
>> giving into "Optimizing for the current technology" rather than "There
>> is something profound going on here".
>
> is that how you interpreted my sort/select anecdote?
No definitely not. The bit I object to is purely limited to your phrase
"involving the fewest number of keystrokes".
I honestly believe your sort/select anecdote reveals the presence of
something a lot more powerful going on than that.
Elucidating what that powerful meme is, is what is important here.
> i certainly wasn't thinking at all about the quite different
> problem of "optimizing for current technology".
Keystrokes are so ubiquitously part of "our current technology" that we
often fail to see them as merely "part of our _current_ technology".
I certainly can foresee a future utterly lacking in keystrokes.
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email :
john.carter@...
New Zealand
"The notes I handle no better than many pianists. But the pauses
between the notes -
ah, that is where the art resides!' - Artur Schnabel
William Tanksley, Jr — 2005-02-08 04:22:08
On Sun, 6 Feb 2005 17:21:57 -0500, stevan apter <
sa@...> wrote:
> From: "Slava Pestov" <slava@...>
> > I've been watching this thread with interest, but I think you guys are
> > taking the wrong approach. Instead of asking "how can I make a complex
> > stack effect manageable?" you should be asking "how can I _simplify_ the
> > stack effect?" Indeed, not everything has to be kept on the stack...
> me too, but i've drawn a different conclusion. the computation,
> written in "math", is clear and easy to understand,
Have you taught algebra? I have. People do not find "math" clear or
easy to understand until they are taught it and drilled in it.
Amusingly enough, after I taught algebra and learned Forth, I took a
series of classes in abstract algebra from a Forth programmer, who
taught from an algebra text that used RPN for its notation. For our
purposes, RPN was the easiest to manipulate.
> but without
> the ability to name and reuse parts of the stack, the resulting
> concatenative programs are obscure and hard to grasp.
Concatenative languages don't offer that (at least not that we know
of); instead they offer ways to name and reuse transformations of
dataflow. You lose some advantages, you win others.
> the math
> is a single line, and unless the code is as well, it will be
> difficult to persuade people of the virtues of the concatenative
> approach.
I don't believe that any language, concatenative or otherwise, will be
simpler than every other language for all possible problems. I could
be wrong. This is a problem that elegantly illustrates the
shortcomings of concatenative languages.
> names are good. and they don't violate what billy has called "the
> concatenativity test":
> "a language is concatenative if any two valid programs can be
> concatenated to form a valid program, and any valid program can
> be split at any lexical boundary to form two valid programs."
> depending on what "valid" means, one might have to adjust the
> meaning of "lexical boundary".
I did indeed say this. :-) But I don't see how you can adjust this
without losing concatenativity within the subset of the language that
allows named data -- in short, you have to create a mini-language.
> alternatively, given a pattern
> and a program, such as
> [a b c]
> b -: a c * 4 * b 2 ^ -. .5 ^ [1 -1] * + a 2 * %
> eliminate names by transforming the code into "pure" concatenative
> code, viz. code in which the names are replaced by the implied stack
> manipulations. i've been meaning to write this for a while.
I worked on this a while ago, and didn't do very well. It's definitely
possible, though. Oh, and I'd also say that it's a good thing. But
it's not concatenative; it's just implementing a language parser
inside a concatenative language.
-Billy
William Tanksley, Jr — 2005-02-08 15:57:54
On Sun, 6 Feb 2005 17:29:38 -0500, stevan apter <
sa@...> wrote:
> problem 2 is the fact that the +/- is hard to factor in a
> convincing, transparent way.
It's pretty easy -- just treat the formula as two seperate terms. Rather than
(-b +/- sqrt(b^2-4ac)) / 2a
Rewrite it as
-b/2a +/- sqrt(b^2-4ac)/2a
Now you compute the two terms separately, and the final operation is a
+/- to combine the two terms to produce the two results.
-Billy
William Tanksley, Jr — 2005-02-08 16:01:07
Martin Young <
martin@...> wrote:
> My feeling is that if the convatenative approach ever takes off then
> it will be as a "bottom layer" language which is suitable for
> analysis, optimisation and other (semi)automatic optimisation. The
> actual programming will be done by higher level languages and
> tools. The higher level tools may even be lambda detived languages.
This hardly qualifies as prophecy, since it's already happened; see
Postscript. It's probably the most written programming language in
existance (assuming that you define "most written" to include programs
written by other programs), since so many printers use it. Of course,
I get your point; there's a lot more that we can do.
I think you know, though, that I have additional hopes. I also don't
agree with your definition of "higher level", because I think that
describes what things you can describe in a language, not what syntax
it uses. Joy is a higher level language than Forth; Joy is higher
level than C. Forth is in some ways higher level than C.
> Martin.
-Billy
William Tanksley, Jr — 2005-02-08 16:49:42
On Sun, 6 Feb 2005 17:24:57 -0500, stevan apter <
sa@...> wrote:
> From: "Slava Pestov" <slava@...>
> > What about having objects on the stack, where an object contains several
> > named fields, as in Factor?
> in my opinion, too much setup is required. the quadratic
> algorithm takes three numbers, not an object containing
> three name-number pairs.
No "object" needed, in the sense of name-number pairs. A tuple is
enough, and gets better results; after all, mathmaticians don't really
use names (the letters they do use keep proving that names only get in
the way).
The funny thing is that the purpose of the quadratic formula is to
find the zeros of a second-degree polynomial; in other words, although
the quadratic formula is a function of three variables, the actual
problem is a function of only one input. So taking a tuple -- or a
polynomial -- as an input is appropriate to the nature of the problem.
So, I thrashed out a function that used a tuple to perform the
operations, and then looking at it I realised that the tuple was just
getting in the way, so I created another version without the tuple.
Here's the tuple version:
The tuple indexers here return ( x tuple ), so the tuple's on the top
of the stack.
+/- = ( a b -- sum diff )
ab--abab + abs--sab - ;
discriminant = ( a b c -- a d )
abc--abac * 4 * swap squared - neg ;
solve-quadratic = ( a b c -- r1 r2 )
triple ( form a 3-tuple )
2nd 1st abt--tba halved / neg ( form first term )
swap ( get the tuple on top )
1st 2nd 3rd drop discriminant sqrt halved swap /
+/-
;
I think that's right.
Here's a transformation of that into pure stack operations:
discriminant = ( a b c -- d )
abc--bac * 4 * swap squared - neg ;
solve-quadratic = ( a b c -- r1 r2 )
abc--abcba halved / neg ( form first term )
abct--taabc ( bury the first term )
discriminant sqrt halved swap /
+/-
;
I went a little bit out of my way to keep the word 'discriminant', but
I think that's useful. If I didn't bother keeping it, I could simplify
some of the stack manipulations.
I'm not going to argue that this is "better" than the algebraic
version. It's a procedural account of one way to compute the quadratic
formula; I don't know how numerically stable it is. A good numeric
programmer would probably do it differently (and might produce
something considerably longer). A programmer who did a LOT of
numerical programming might write a little sublanguage that allowed
him to express the quadratic equation using his favorite notation
(which might or might not be infix), and then would automatically
apply his numerical programming expertise to produce a numerically
stable result.
However, I will argue that this is a reasonably terse and clear
procedural description of *how* to compute the quadratic equation. One
advantage of being so explicit on the *how* is that the
above-mentioned expert can clearly see whether or not I'm making
stability mistakes.
-Billy
William Tanksley, Jr — 2005-02-08 17:26:23
Nick Forde <
nickf@...> wrote:
> On 7 Feb 2005, at 17:36, sa@... wrote:
> > "William Tanksley, Jr" <wtanksleyjr@...> wrote on 02/07/2005
> > 10:57:30AM:
> >> I don't like local variables (nor, of course, globals). They don't
> >> preserve the concatenative property, and don't seem to add any
> >> interesting new property; their only advantage is that they're what
> >> people trained in lambda-based languages naturally expect. I suspect
> >> them of being a cop-out that's hiding a much better solution from us.
I didn't get sa's email, so here's my brief reply.
>>i'm not sure i understand what you mean by "local variable".
A binding of a name to a specific datum, which is unique for each call
to the function containing the "local variable".
>>the
>>ability to associate a name with a position on the stack, and then
>>use that name to denote the value at that position, seems to me
>>to be the most natural and direct way of reducing stack noise in
>>the code.
I'd question how natural and direct that really is -- I've only seen
that implemented once, in F-PC, and I've never seen anyone actually
use it. The problem is that the data at a given position on the stack
changes all the time -- that's the nature of a stack. So you can name
a stack position (let's say the position two integers from the top)
'X', but two words later there could be anything else at that stack
position. The same problem arises if you name positions relative to
the bottom of the stack.
Local variables are far more common and useful (the difference is that
a local variable definition *removes* the value from the stack, and
places it into a holding area where it can be reliably accessed). The
problem is that they're a non-native feature; there are no rules that
apply to them in concatenative theory (they come from lambda theory).
Using them in a concatenative language is like using mutable variables
in a functional applicative language; lambda theory doesn't allow for
that.
> I agree. I can't think of any situation where an immutable stack
> reference couldn't be replaced by a series of equivalent stack
> operators.
There aren't any such situations -- the process of parsing a
lambda-based language is defined mathematically in stack-machine
terms. It's therefore definitely always possible to convert from
lambda to stack.
> As I said in an old thread on this list I think being able to choose the
> most appropriate solution gives the best of both worlds and I think it
> can lead to clearer code.
You're right.
> Currently I find programming in concatenative languages a bit like
> mining for diamonds. Most of the time is spent covering rough
> ground but every so often I stumble across a gem that reminds me
> that the rewards can be great.
That's a good description.
> I have faith that Billy is right and there is a better solution out there
> somewhere.
Persist, my brother! Repent and believe and you SHALL be saved! :-)
I think there are better solutions; and I also think that practice
makes perfect. Much, perhaps most, of the difficulties you now face
are caused by unfamiliar concepts, not by a lack of theory.
> But maybe that's just a greedy prospectors blind optimism :-)
Well, you know who always makes the big bucks in any gold rush -- the
storekeepers, not the prospectors. So ask yourself, what is Billy
selling? :-)
> Nick.
-Billy
William Tanksley, Jr — 2005-02-08 17:34:10
On Sun, 06 Feb 2005 17:43:19 -0500, Slava Pestov <
slava@...> wrote:
> I think the concatenative approach works well for many problems. The
> only difficulty I've found is with mathematical formulas; everything
> else seems to break down into object oriented and functional
> abstractions that are expressed nicely in concatenative code.
True. Mathematical formulas aren't procedures, and most concatenative
theory is expressed and understood procedurally. Our current
understanding isn't sufficient to make useful algebraic manipulations
in a concatenative language.
I don't know if our knowledge will ever be enough. We may always have
to use sublanguages to express equations and such. But I do know that
there *are* some algebraic manipulation rules for concatenative
languages out there that we don't know now, so while we press to find
them I can always hope that one of them will break the barrier.
> Slava
-Billy
sa@dfa.com — 2005-02-08 20:34:52
"William Tanksley, Jr" <
wtanksleyjr@...> wrote on 02/08/2005 11:49:42
AM:
:
>
> discriminant = ( a b c -- d )
> abc--bac * 4 * swap squared - neg ;
>
> solve-quadratic = ( a b c -- r1 r2 )
> abc--abcba halved / neg ( form first term )
> abct--taabc ( bury the first term )
> discriminant sqrt halved swap /
> +/-
> ;
>
keeping as close to your notational preferences as possible,
in XY that would be:
; +/- [1 -1] * + ;
; squared 2 ^ ;
; sqrt .5 ^ ;
; halved .5 * ;
; neg -: ;
; discriminant
{ [a b c] b a c } * 4 * swap squared - neg ;
; solve-quadratic
{ [a b c] a b c b a } halved % neg
{ [a b c t] t a a b c } discriminant sqrt halved swap
+/- ;
as it stands, my code doesn't give the correct result, but
i don't have the time right now to figure out why.
in what way does your "A--B" notation differ from my use
in this example of pattern-matching against the stack?
William Tanksley, Jr — 2005-02-08 21:14:34
On Tue, 08 Feb 2005 09:26:32 +1300 (NZDT), John Carter
<
john.carter@...> wrote:
> On Mon, 7 Feb 2005, William Tanksley, Jr wrote:
> > John Carter <john.carter@...> wrote:
> >> The reason why I'm going through these contortions is to try somehow
> >> keep the nifty concatenative property that makes rewriting Joy such an,
> >> umm, Joy, and lose some of the endless dup swap over stack fiddling.
> > I agree. I'd even more like to figure out how to express the
> > properties of some of the extensions to the concatenative languages
> > we've been looking at -- extensions like the return stack
> > manipulations, continuations, source manipulations, and so on. I'd
> > like to come up with something as simple and elegant as
> > concatenativity for them.
> I'm trying hard to figure out whether what Manfred has done is something
> deep, profound and fundamental about the universe.... Or whether it is a
> mere curious side-effect of the particular technology I'm using. (Strings
> of char's on a PC screen.) My grouchy cynic nature keeps muttering its a
> trick, a mere sleight of hand. But part of me keeps eying those Algebra
> for Joy and rewriting Joy and Joy0 pages and going "Wow!"
It's no more a trick than algebra. Yes, we represent both as strings
of chars on a PC screen; but any other representation would preserve
the same properties.
> I keep saying it in my head, over and over, "Functional composition in a
> Concatenative language is....X"
> Where the first suggestion for X is "string concatenation", not quite
> true, since if A and B are fragments of Joy, then A whitespace B not AB is
> the functional composition of B(A()).
How about "concatenation preserving tokens"? In other words, you can't
run tokens together when joining functions, and you can't cut a token
in half when splitting a function. The definition of "token" is left a
bit ambiguous, but for joining functions which are represented as
strings you can always preserve tokens simply by inserting a space
character.
> Thus perhaps X is "List concat".
> "Function Composition in a concatenative language is List Concatenation"
In order for that to make sense, of course, you have to specify that
the representation of source is lists of tokens.
> On the other I can equally write...
> "Functional composition in Lispish languages is List nesting".
This is true (with the above caveat about first defining the
representation of the source, which has to be much more complicated
than it is with a concatenative language), but it doesn't lead to as
many useful results, for two reasons. First, there's many ways to nest
a pair of lists but only two ways to concatenate them; second, Lisp
functions can accept multiple inputs, but composition only allows them
to pass a single one.
> ie. What we are talking about is in a sense, merely a coincidence between
> the base data-structures of the language and a fundamental mathematical
> operation.
> Admittedly the more such coincidences you can arrange/design, and the more
> fundamental the mathematical operations are, the more fun is your
> language.
That's an interesting perspective. Of course, I don't think that it's
reasonable to call a designed event a "coincidence", but I see what
you mean and agree, with the addition that your mathematical
coincidence should describe as much of the language as possible, not
just a little part of it. This is why functional languages are fun to
program in and easier to analyse; they obey the rule of lambda
abstraction that non-functional applicative languages don't
(parameters must not change). Concatenative languages have no such
rule, by the way.
> > I don't like local variables (nor, of course, globals). They don't
> > preserve the concatenative property, and don't seem to add any
> > interesting new property; their only advantage is that they're what
> > people trained in lambda-based languages naturally expect. I suspect
> > them of being a cop-out that's hiding a much better solution from us.
> An important property of lambda based languages is that _every_
> call to a new word is essentially wrapped in an "infra", limiting the
> (potential) damage to the stack.
That can easily be done to stack based languages -- simply require
every function to declare its stack effect, and model the stack
effects at compile time (a very simple constant-time per token
operation). You can make your checking as restrictive or loose as you
want. One person added a polymorphic type checker to Forth this way.
> John Carter Phone : (64)(3) 358 6639
-Billy
John Cowan — 2005-02-08 21:27:00
William Tanksley, Jr scripsit:
> That can easily be done to stack based languages -- simply require
> every function to declare its stack effect, and model the stack
> effects at compile time (a very simple constant-time per token
> operation). You can make your checking as restrictive or loose as you
> want. One person added a polymorphic type checker to Forth this way.
How much of Joy would we lose if all functions had to have their stack
effects fixed at compile time?
--
"Well, I have news for our current leaders John Cowan
and the leaders of tomorrow: the Bill of
jcowan@...
Rights is not a frivolous luxury, in force
http://www.ccil.org/~cowan
only during times of peace and prosperity.
http://www.reutershealth.com
We don't just push it to the side when the going gets tough." --Molly Ivins
John Carter — 2005-02-08 23:05:28
On Tue, 8 Feb 2005, John Cowan wrote:
> William Tanksley, Jr scripsit:
>
>> That can easily be done to stack based languages -- simply require
>> every function to declare its stack effect, and model the stack
>> effects at compile time (a very simple constant-time per token
>> operation). You can make your checking as restrictive or loose as you
>> want. One person added a polymorphic type checker to Forth this way.
>
> How much of Joy would we lose if all functions had to have their stack
> effects fixed at compile time?
Joy is amazingly easy to reason about small fragments of Joy.
It is surprisingly hard to reason about large fragments of Joy.
So Answer 1 to your Question is:
Too Much.
Answer 2:
If we fudge the definition of function slightly. Nothing, and gain quite
a lot.
Consider the disassembly listing of a chunk of C. Follow it from just
before a function call into the function and out again.
Before the function call we have a bunch of direct primitive machine
code instructions.
Then we construct a stack frame holding a frame pointer to the
previous stack frame and the arguments for the function.
On entry into the function we extend the stack frame to make room for
any local variables (temporary scratch space).
Then we have a bunch of primitive operations that do the
work (disregarding static and global variables for the moment)
that operate entirely within that stack frame.
On exit, the we punch the return value into the bottom of the stack
frame (or just leave it in a register depending on the ABI) and just
drop all parameters and local variables off the stack and retrieve the
previous frame pointer.
There after its a bunch of direct machine code instructions doing stuff.
The compiler enforces the rule that we never ever violate our stack
frame, and always tidies it up on exit.
So perhaps what we need in Joy-like languages is an easily
visible _and enforced_ distinction between stack frame mangling
operations, and fixed finite stack effect operations.
It just makes reasoning about larger Joy programs easier.
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email :
john.carter@...
New Zealand
"The notes I handle no better than many pianists. But the pauses
between the notes -
ah, that is where the art resides!' - Artur Schnabel
Slava Pestov — 2005-02-09 02:07:00
John Cowan wrote:
>
> How much of Joy would we lose if all functions had to have their stack
> effects fixed at compile time?
>
In the current Factor source tree, 1830 words out of 2700 have a
statically known stack effect. IMHO, this is pretty good. The remaining
words fall into three general categories:
- Code that uses continuations at some level, eg I/O
- Code that pulls quotations out of namespaces and calls them.
- Code that generates quotations on the fly, and calls them.
While stack effect inference is working well, I'm finding type inference
hard to implement with relatively little gain -- the language is so
dynamic, that for many words all you know is that they take a fixed
number of objects, and produce a fixed number of objects.
Slava
phimvt@lurac.latrobe.edu.au — 2005-02-09 04:08:59
There has been a surprising lot of discussion on the subject
of the quadratic formula, including some ingenious concatenative
solutions to the problem. It is only fair that I should let
my solution see the light of day. Here it is:
DEFINE
quadratic-2 == # a b c => [root1 root2 ]
[ [ [ pop pop 2 * ] # divisor
[ pop 0 swap - ] # minusb
[ swap dup * rollup * 4 * - sqrt ] ] # radical
[i] map ]
ternary i
[ [ [ + swap / ] # root1
[ - swap / ] ] # root2
[i] map ]
ternary.
The overall structure is about the same as most other programs I have seen
on the web. The particular names in the comments on the right are taken
which "simulates" let expressions (which of course do introduce names).
All details of the program are to be in a new web page. I have most of it
written, but I have to investigate why it is that the first version of
that web page is not visible on the web. I conjecture that our systems
administrators are now acting as censors to weed out undesirable material,
and that the censor is overworked. I have not written a new web page for a
very long time, and the I am not familiare with new regulations. So I must
investigate.
Sorry about the delay.
- Manfred
John Carter — 2005-02-09 05:08:35
On Wed, 9 Feb 2005
phimvt@... wrote:
>
>
> There has been a surprising lot of discussion on the subject
> of the quadratic formula, including some ingenious concatenative
> solutions to the problem. It is only fair that I should let
> my solution see the light of day. Here it is:
>
> DEFINE
> quadratic-2 == # a b c => [root1 root2 ]
> [ [ [ pop pop 2 * ] # divisor
> [ pop 0 swap - ] # minusb
> [ swap dup * rollup * 4 * - sqrt ] ] # radical
> [i] map ]
> ternary i
> [ [ [ + swap / ] # root1
> [ - swap / ] ] # root2
> [i] map ]
> ternary.
Ok, so (and I have checked it out on Joy)...
[ [ pop pop 2 * ] # divisor
[ pop 0 swap - ] # minusb
[ swap dup * rollup * 4 * - sqrt ] ] # radical
[i] map
...works.
But given the description...
map : A [P] -> B
Executes P on each member of aggregate A, collects results in
same type aggregate B.
...I don't see how it works.
Does map dup the whole stack before each and every execution of P?
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email :
john.carter@...
New Zealand
"The notes I handle no better than many pianists. But the pauses
between the notes -
ah, that is where the art resides!' - Artur Schnabel
William Tanksley, Jr — 2005-02-09 16:09:34
On Tue, 8 Feb 2005 15:34:52 -0500,
sa@... <
sa@...> wrote:
> "William Tanksley, Jr" <wtanksleyjr@...> wrote on 02/08/2005 11:49:42
> AM:
> as it stands, my code doesn't give the correct result, but
> i don't have the time right now to figure out why.
It's probably because my algorithm was wrong -- I'm pretty bad about
that when I don't bother testing. That's okay, I'm just going for the
idea.
> in what way does your "A--B" notation differ from my use
> in this example of pattern-matching against the stack?
I like your notation.
Only two differences:
1. Mine is terser, but conveys the same info.
2. Yours can be expanded to convey non-concatenative meaning.
In other words, my "syntax" is just another way of spelling the names
of stack manipulation words; your syntax is a full programming
language.
I like your syntax when it's used in a concatenative way. I don't like
having to read through it to figure out whether or not it's being used
that way this time.
I do agree with you (reluctantly) that having a simple lambda feature
available is very handy, given our current lack of understanding about
concatenativity. I'm not sure that I would want the only stack
shuffling feature to look exactly like the lambda, for two reasons:
1. It encourages use of the lambda feature by people making
modifications to the shuffle code.
2. It's not immediately evident whether a given expression is a lambda
or just a simple shuffle.
-Billy
William Tanksley, Jr — 2005-02-09 16:16:53
On Tue, 08 Feb 2005 21:07:00 -0500, Slava Pestov <
slava@...> wrote:
> John Cowan wrote:
> > How much of Joy would we lose if all functions had to have their stack
> > effects fixed at compile time?
A dialect of Forth was made that did this; it worked perfectly well,
but no loop or conditional was allowed to be "unbalanced". This would
be less of a loss in a language which supported dynamic arrays, but
even in a very primitive environment, the language was quite usable.
> In the current Factor source tree, 1830 words out of 2700 have a
> statically known stack effect. IMHO, this is pretty good. The remaining
> words fall into three general categories:
> - Code that uses continuations at some level, eg I/O
I can imagine typechecked continuations. I think that would actually
be pretty convenient, since after all the source and the dest of every
continuation must have congruent stacks.
> - Code that pulls quotations out of namespaces and calls them.
> - Code that generates quotations on the fly, and calls them.
StrongForth solved this by adding a type signature to quotation
literals; but it didn't provide a way to generate quotations. It did
provide a way to evaluate strings, which is similar; the evaluator had
to be passed the string and the expected type signature.
> While stack effect inference is working well, I'm finding type inference
> hard to implement with relatively little gain -- the language is so
> dynamic, that for many words all you know is that they take a fixed
> number of objects, and produce a fixed number of objects.
Is that a problem? If your type system is polymorphic, it's possible
that this is all you need to know.
> Slava
-Billy
sa@dfa.com — 2005-02-09 17:29:19
it occurred to me on the drive in this morning that i could support
your shuffle notation in XY with very little bother: an undefined
symbol of the form A--B is treated as shorthand for { [A*] B* }, where
X* is the result of prefixing a space to each x in X.
so we have:
; squared 2 ^ ;
; +/- [1 -1] * + ;
; sqrt .5 ^ ;
; halved .5 * ;
; neg -: ;
; discriminant
abc--bac * 4 * swap squared - neg ;
; solve-quadratic
abc--abcba halved % neg
abct--tabc discriminant sqrt halved swap
+/- ;
which, of course, still gives the wrong answer. :)
i still think it would be nice to have a way of automatically
converting lambdas to name-free expressions in an arbitrary
complete base B, where operations in B are annotated with
their stack-effects (along with the reverse conversion.)
"William Tanksley, Jr" <
wtanksleyjr@...> wrote on 02/09/2005 11:09:34
AM:
>
> On Tue, 8 Feb 2005 15:34:52 -0500, sa@... <sa@...> wrote:
> > "William Tanksley, Jr" <wtanksleyjr@...> wrote on 02/08/2005
11:49:42
> > AM:
>
> > as it stands, my code doesn't give the correct result, but
> > i don't have the time right now to figure out why.
>
> It's probably because my algorithm was wrong -- I'm pretty bad about
> that when I don't bother testing. That's okay, I'm just going for the
> idea.
>
> > in what way does your "A--B" notation differ from my use
> > in this example of pattern-matching against the stack?
>
> I like your notation.
>
> Only two differences:
>
> 1. Mine is terser, but conveys the same info.
> 2. Yours can be expanded to convey non-concatenative meaning.
>
> In other words, my "syntax" is just another way of spelling the names
> of stack manipulation words; your syntax is a full programming
> language.
>
> I like your syntax when it's used in a concatenative way. I don't like
> having to read through it to figure out whether or not it's being used
> that way this time.
>
> I do agree with you (reluctantly) that having a simple lambda feature
> available is very handy, given our current lack of understanding about
> concatenativity. I'm not sure that I would want the only stack
> shuffling feature to look exactly like the lambda, for two reasons:
>
> 1. It encourages use of the lambda feature by people making
> modifications to the shuffle code.
> 2. It's not immediately evident whether a given expression is a lambda
> or just a simple shuffle.
>
> -Billy
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
William Tanksley, Jr — 2005-02-09 18:47:33
On Wed, 9 Feb 2005 12:29:19 -0500,
sa@... <
sa@...> wrote:
> i still think it would be nice to have a way of automatically
> converting lambdas to name-free expressions in an arbitrary
> complete base B, where operations in B are annotated with
> their stack-effects (along with the reverse conversion.)
Yes, it would be. It's not simple, though.
-Billy
Ivan Tomac — 2005-02-09 23:35:37
--- In
concatenative@yahoogroups.com, John Carter <john.carter@t...>
wrote:
> On Mon, 7 Feb 2005, Ivan Tomac wrote:
>
> >> And here is the cunning bit that makes it different from the
> > usual...
> >>
> >> Since each program operates step by step with it's neighbour,
also
> > add
> >> operators dup_left (and dup_right) that dup whatever is on top
> >> of the right(left) hand stack and push it onto the current stack.
> >>
> >> I'm sure other arrangements can be imagined, some might even be
> > useful...
> >>
> >
> > Very interesting idea. But how would you know what would dup_left
or
> > dup_right come up with on the stack? Wouldn't that require all
the top
> > level functions in each program to execute at the same speed?
>
> Not really, it just means you have to execute one operator from
each
> program in each time step, and you have to cache what is on top of
all
> stacks before you start executing the next step.
>
> > Also
> > what happens to functions called from within the top level
functions:
> > are they also allowed to call dup_left as well?
>
> One operator per step would be the rule.
>
But what consitutes as one operator? What happens if you have a user
defined operator that takes one hour to execute? Do the other threads
have to wait for it to finish? Or are you limited to just using simple
operators?
Sounds a bit too restrictive to me.
> > Side-effects would also be another problem with code that runs in
> > parallel although that's the case with my version of threads as
well
> > if it was in fact implemented to execute all the programs
> > simultaneously.
>
> I think if we shorten that paragraph to
> "Side-effect be a problem"
> and we have it in a nut shell.
>
> My favourite answer is, "Well don't do that then."
>
Again sounds too restrictive. I don't like avoiding problems like
these cause they introduce special cases and sometime limit the design
or evolution of the language later on. But I think I have a simple
solution that may work.
There has been a fair bit of talk about the stack shuffling notation.
I was a bit resistant to the idea at first but I'm really starting to
like it - so much so that I'm wondering if we need combinators like
dup and pop/drop in a language that supports stack shuffling (dip on
the other hand is still useful) - it just seems redundant and
inconsistent to be using stack shuffling for certain things and simple
combinators for other things. On the other hand writing swap instead
of ab--ba seems more straight forward and can sometimes help make the
code more readable...
Anyway back to threads. After thinking a bit about this and thinking
about the shuffle notation I thought that maybe threads could be
created by using a new construct in a concatenative language.
We'd have to treat the constructs as atoms to preserve concatenation
as if I understand this correctly that's the reason why Billy's syntax
for stack shuffling doesn't break the concatenative properties of the
language.
So for example +- (or +/-) could be written as:
+- == ab--abab <+ ->
<+ -> is an example of the construct. The language would have to work
backwards to figure out how many parameters each function inside the
construct takes. So + would then be applied to the first 'ab' (as in
the two elements burried on the stack) and - to the second (the two
elements on top) - the results then get combined on the stack.
For this to work the language obviously needs to be able to check how
many parameters each function needs for execution.
There is no reason why + and - couldn't run in parallel. After all
their inputs and outputs are independant. But what happens when we
have a function that performs IO?
In the current implementation of Joy there are no threads and
execution is always well defined. There is no reason to break the
execution model. To add support for threads without breaking the
execution model each function that performs IO should have it's own
delayed IO buffer. Once all the threads have completed execution the
IO buffers themselves get concatenated and 'executed' in such a way
that the first thread's IO buffer executes first, the second thread's
second and so on.
The end result is the same as if the whole sequence has been executed
without using threads.
Now for the quadratic function implemented using this:
(* -b +/- sqrt(b^2 - 4 * a * c)
----------------------------
2 * a
term1 = sqrt(b^2 - 4 * a * c)
term2 = -b
term3 = 2 * a
*)
quad ==
4 abcn--abbbanc [<* *>] dip * - sqrt (* a b term1 *)
[2 swap <* neg>] dip (* 2a -b term1 *)
abt--btabta <+ id - id> </ /> ;
Note the use of id in the last line - it takes one element from the
stack and returns that same element (it's a bit different from Joy's
current definition of id).
Ivan
Ivan Tomac — 2005-02-10 00:20:41
>
> quad ==
> 4 abcn--abbbanc [<* *>] dip * - sqrt (* a b term1 *)
> [2 swap <* neg>] dip (* 2a -b term1 *)
> abt--btabta <+ id - id> </ /> ;
>
Now that I think about it there is no reason to have the <> construct
at all. Instead we could introduce the t combinator and rewrite the
above as:
quad ==
5 abcn--abbbanc [[* *] t] dip * - sqrt
[2 swap [* neg] t] dip
abt-btabta [+ id - id] t [/ /] t ;
Slava Pestov — 2005-02-10 00:53:42
William Tanksley, Jr wrote:
>>While stack effect inference is working well, I'm finding type inference
>>hard to implement with relatively little gain -- the language is so
>>dynamic, that for many words all you know is that they take a fixed
>>number of objects, and produce a fixed number of objects.
>
>
> Is that a problem? If your type system is polymorphic, it's possible
> that this is all you need to know.
I'm hoping to use type inference to improve performance of compiled code
by reducing the frequency of dynamic dispatch.
Slava
Ivan Tomac — 2005-02-10 01:47:30
--- In
concatenative@yahoogroups.com, "Ivan Tomac" <e1_t@y...> wrote:
>
> >
> > quad ==
> > 4 abcn--abbbanc [<* *>] dip * - sqrt (* a b term1 *)
> > [2 swap <* neg>] dip (* 2a -b term1 *)
> > abt--btabta <+ id - id> </ /> ;
> >
>
> Now that I think about it there is no reason to have the <>
construct
> at all. Instead we could introduce the t combinator and rewrite the
> above as:
>
> quad ==
> 5 abcn--abbbanc [[* *] t] dip * - sqrt
> [2 swap [* neg] t] dip
> abt-btabta [+ id - id] t [/ /] t ;
It just occured to me that a sequence of t combinators can also be
used to get rid of most
of the shuffle notation, for example:
abcn--abbbanc -> [over swap] t [[dup dup] id id id] t
Ivan
Ivan Tomac — 2005-02-10 02:27:06
--- In
concatenative@yahoogroups.com, "Ivan Tomac" <e1_t@y...> wrote:
>
> It just occured to me that a sequence of t combinators can also be
> used to get rid of most
> of the shuffle notation, for example:
>
> abcn--abbbanc -> [over swap] t [[dup dup] id id id] t
Or better yet:
abcn--abbbanc -> [[over dupd dupd] swap] t
Ivan
William Tanksley, Jr — 2005-02-10 19:29:35
On Wed, 09 Feb 2005 19:53:42 -0500, Slava Pestov <
slava@...> wrote:
> William Tanksley, Jr wrote:
> >>While stack effect inference is working well, I'm finding type inference
> >>hard to implement with relatively little gain -- the language is so
> >>dynamic, that for many words all you know is that they take a fixed
> >>number of objects, and produce a fixed number of objects.
> > Is that a problem? If your type system is polymorphic, it's possible
> > that this is all you need to know.
> I'm hoping to use type inference to improve performance of compiled code
> by reducing the frequency of dynamic dispatch.
So is your type system polymorphic? If not, it should be. This can be
done staticly as easily as it can be done dynamicly. Perhaps a bit
easier. See
http://web.archive.org/web/20030813044512/home.t-online.de/home/s.becher/forth/
for details and a sample implementation.
> Slava
-Billy
William Tanksley, Jr — 2005-02-10 20:00:55
On Thu, 10 Feb 2005 02:27:06 -0000, Ivan Tomac <
e1_t@...> wrote:
> --- In concatenative@yahoogroups.com, "Ivan Tomac" <e1_t@y...> wrote:
> > It just occured to me that a sequence of t combinators can also be
> > used to get rid of most of the shuffle notation, for example:
> > abcn--abbbanc -> [over swap] t [[dup dup] id id id] t
> Or better yet:
> abcn--abbbanc -> [[over dupd dupd] swap] t
I'm not sure what's happening here, but I admire the effect. What's
the t combinator?
I think I could get used to reading that type of stack notation; the
one problem is that it doesn't provide a pretty stack picture, but on
the other hand it's obviously fully concatenative.
What's the smallest complete base that includes the t combinator?
What's the smallest complete base that includes the t combinator and
implements the standard Joy combinators in the shortest and cleanest
possible way? (I know, that's not computable, but I'd still like to
see an example.)
Can the t combinator execute operations which have more than a stack
rearrangement effect? If so, this could be the answer to the questions
people were asking about how to mechanically translate lambda code to
concatenative code and back.
> Ivan
-Billy
Slava Pestov — 2005-02-10 20:19:01
William Tanksley, Jr wrote:
>>I'm hoping to use type inference to improve performance of compiled code
>>by reducing the frequency of dynamic dispatch.
>
>
> So is your type system polymorphic? If not, it should be. This can be
> done staticly as easily as it can be done dynamicly. Perhaps a bit
> easier. See http://web.archive.org/web/20030813044512/home.t-online.de/home/s.becher/forth/
> for details and a sample implementation.
I'm not sure what you mean. Strongforth is statically typed.
Slava
Ivan Tomac — 2005-02-10 21:19:02
--- In
concatenative@yahoogroups.com, "William Tanksley, Jr"
<wtanksleyjr@g...> wrote:
> On Thu, 10 Feb 2005 02:27:06 -0000, Ivan Tomac <e1_t@y...> wrote:
>
> > Or better yet:
> > abcn--abbbanc -> [[over dupd dupd] swap] t
>
> I'm not sure what's happening here, but I admire the effect. What's
> the t combinator?
>
I've already described it in the following 2 posts:
http://groups.yahoo.com/group/concatenative/message/2234
http://groups.yahoo.com/group/concatenative/message/2235
Initial goal was to implement some form of a simple but purely
functional threading
combinator (which is also why I decided to call it the t combinator)
that would run a
number of threads and and produce consistent results every time it's
ran.
> I think I could get used to reading that type of stack notation; the
> one problem is that it doesn't provide a pretty stack picture, but
on
> the other hand it's obviously fully concatenative.
>
> What's the smallest complete base that includes the t combinator?
>
> What's the smallest complete base that includes the t combinator and
> implements the standard Joy combinators in the shortest and cleanest
> possible way? (I know, that's not computable, but I'd still like to
> see an example.)
>
I haven't had enough time to think about that. The t combinator can
be thought of as a
generalized version of the dip combinator though:
a [b] dip == a [b id] t
> Can the t combinator execute operations which have more than a stack
> rearrangement effect? If so, this could be the answer to the
questions
> people were asking about how to mechanically translate lambda code
to
> concatenative code and back.
>
Yes, as I've explained in the two previously mentioned posts.
> -Billy
Ivan
Ivan Tomac — 2005-02-10 21:25:02
--- In
concatenative@yahoogroups.com, "Ivan Tomac" <e1_t@y...> wrote:
>
> > Can the t combinator execute operations which have more than a stack
> > rearrangement effect? If so, this could be the answer to the
> questions
> > people were asking about how to mechanically translate lambda code
> to
> > concatenative code and back.
> >
>
> Yes, as I've explained in the two previously mentioned posts.
>
What I meant is yes, it can execute operations that have more then a stack rearrangement
effect but I'm not sure how much it will help with translating concatenative languages to
lambda based ones. I'll have to think more about that.
stevan apter — 2005-02-10 22:23:34
so let me see if i understand this.
10 20 30 [[2 +] [3 4 * +] [*]] t
would do
10 2 + -> 12 -> 12
10 20 3 4 * + -> 10 32 -> 32
10 20 30 * -> 10 600 -> 600
so the stack would now be
12 32 600
----- Original Message -----
From: "Ivan Tomac" <e1_t@...>
To: <concatenative@yahoogroups.com>
Sent: Thursday, February 10, 2005 4:19 PM
Subject: [stack] Re: The annoying quadratic formula
>
>
> --- In concatenative@yahoogroups.com, "William Tanksley, Jr"
> <wtanksleyjr@g...> wrote:
> > On Thu, 10 Feb 2005 02:27:06 -0000, Ivan Tomac <e1_t@y...> wrote:
> >
> > > Or better yet:
> > > abcn--abbbanc -> [[over dupd dupd] swap] t
> >
> > I'm not sure what's happening here, but I admire the effect. What's
> > the t combinator?
> >
>
> I've already described it in the following 2 posts:
>
> http://groups.yahoo.com/group/concatenative/message/2234
> http://groups.yahoo.com/group/concatenative/message/2235
>
> Initial goal was to implement some form of a simple but purely
> functional threading
> combinator (which is also why I decided to call it the t combinator)
> that would run a
> number of threads and and produce consistent results every time it's
> ran.
>
> > I think I could get used to reading that type of stack notation; the
> > one problem is that it doesn't provide a pretty stack picture, but
> on
> > the other hand it's obviously fully concatenative.
> >
> > What's the smallest complete base that includes the t combinator?
> >
> > What's the smallest complete base that includes the t combinator and
> > implements the standard Joy combinators in the shortest and cleanest
> > possible way? (I know, that's not computable, but I'd still like to
> > see an example.)
> >
>
> I haven't had enough time to think about that. The t combinator can
> be thought of as a
> generalized version of the dip combinator though:
>
> a [b] dip == a [b id] t
>
>
> > Can the t combinator execute operations which have more than a stack
> > rearrangement effect? If so, this could be the answer to the
> questions
> > people were asking about how to mechanically translate lambda code
> to
> > concatenative code and back.
> >
>
> Yes, as I've explained in the two previously mentioned posts.
>
> > -Billy
>
> Ivan
>
>
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
Ivan Tomac — 2005-02-10 23:24:52
--- In
concatenative@yahoogroups.com, "stevan apter" <sa@n...> wrote:
> so let me see if i understand this.
>
> 10 20 30 [[2 +] [3 4 * +] [*]] t
>
> would do
>
> 10 2 + -> 12 -> 12
> 10 20 3 4 * + -> 10 32 -> 32
> 10 20 30 * -> 10 600 -> 600
>
> so the stack would now be
>
> 12 32 600
>
Not quite.
[2 +] needs 1 element on the stack
[3 4 * +] which can be simplified to [12 +] also needs one element on
the stack
[*] needs two elements though
So you'd need 4 numbers on top of the stack:
10 20 30 40 [[2 +] [12 +] [*]] t
which are executed as follows:
thread 1: 10 2 + --> 12
thread 2: 20 12 + --> 32
thread 3: 30 40 * --> 1200
Reason for that is that each thread is independent and doesn't know
about any of the other threads. That way no thread (other than the
caller function) has to wait for the results from any other thread and
the implementation becomes a lot simpler.
So basically executing
10 20 30 40 [[2 +] [12 +] [*]] t
gives
12 32 1200
on stack.
Ivan
John Carter — 2005-02-11 00:17:30
On Thu, 10 Feb 2005, Ivan Tomac wrote:
> Reason for that is that each thread is independent and doesn't know
> about any of the other threads. That way no thread (other than the
> caller function) has to wait for the results from any other thread and
> the implementation becomes a lot simpler.
Ah.
Like Manfred's "map", it seems to be dup'ping the whole stack for each
thread.
In fact what is the difference between t and map i?
I think perhaps the notion is to allow something like dup_left, dup_right
correct?
> So basically executing
>
> 10 20 30 40 [[2 +] [12 +] [*]] t
>
> gives
>
> 12 32 1200
>
> on stack.
>
> Ivan
>
>
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email :
john.carter@...
New Zealand
"The notes I handle no better than many pianists. But the pauses
between the notes -
ah, that is where the art resides!' - Artur Schnabel
Ivan Tomac — 2005-02-11 01:17:24
--- In
concatenative@yahoogroups.com, John Carter <john.carter@t...>
wrote:
> On Thu, 10 Feb 2005, Ivan Tomac wrote:
>
> > Reason for that is that each thread is independent and doesn't know
> > about any of the other threads. That way no thread (other than the
> > caller function) has to wait for the results from any other thread and
> > the implementation becomes a lot simpler.
>
> Ah.
>
> Like Manfred's "map", it seems to be dup'ping the whole stack for each
> thread.
>
No, it doesn't duplicate the whole stack. It only takes the number of
elements required by each function and passes those elements to each
function.
> In fact what is the difference between t and map i?
>
They are completely different.
t takes 1 parameter from the stack, a list of functions. It then finds
out how many parameters each function inside the list requires for
execution. It then applies each function/thread to corresponding
arguments on stack (basically creates a new stack for each function
that only contains the functions parameters).
map on the other hand takes 2 parameters - a list and a function to
iterate over the list and then composes a new list based on the
results of the iterator function.
1 2 3 [[44 +] [55 +] [66 +]] t --> 45 57 69
1 2 3 [[44 +] [55 +] [66 +]] map i --> error
I've been searching for a similar combinator in Joy and found that the
closest thing to t would be the construct combinator but unlike
construct it doesn't pass the whole stack to each function and rather
then returning 1 value from each function it returns all values. It
should be possible to write both in a way that makes them run a
separate thread for each function.
Also as I previously mentioned t can also be thought as a
generalization of the dip combinator:
a [b] dip == a [[b] [id]] t
or more conveniantly a [b id] t
On a side note I think it would be conveniant to be able to write
[[dup] [dup]] t as [dup dup] t but currently Joy doesn't seem to
support this (in other words "2 [dup] first i" fails to execute). But
that's going a bit off-topic.
> I think perhaps the notion is to allow something like dup_left,
dup_right
> correct?
>
Again no. dup_left and dup_right would require interaction between
threads and I think it would also break concatenativity and put
restrictions on code execution (unless I misinterpreted your
explaination of how dup_left and dup_right would work). The behavior
of dup_left and dup_right can be trivially simulated through a
sequence of t combinators and stack shuffling though.
William Tanksley, Jr — 2005-02-11 02:55:55
On Thu, 10 Feb 2005 15:19:01 -0500, Slava Pestov <
slava@...> wrote:
> William Tanksley, Jr wrote:
> >>I'm hoping to use type inference to improve performance of compiled code
> >>by reducing the frequency of dynamic dispatch.
> > So is your type system polymorphic? If not, it should be. This can be
> > done staticly as easily as it can be done dynamicly. Perhaps a bit
> > easier. See
http://web.archive.org/web/20030813044512/home.t-online.de/home/s.becher/forth/
> > for details and a sample implementation.
> I'm not sure what you mean. Strongforth is statically typed.
Yes, it is. That's what it seems you would want -- a polymorphic
static type system, where every static type has a single dynamic
subtype. I'm not telling you to use strongForth's type checking, since
I don't know whether it could be accomodated to inference; but I know
polymorphism can help, especially if you allow the programmer to add
type annotations in specific cases (or even if you require it, like ML
does).
For runtime speedups, check out the research on "inline polymorphic
caching", which studies show speeds up dynamic type dispatch to almost
the speed of a simple indexing.
> Slava
-Billy
John Carter — 2005-02-11 03:05:42
On Fri, 11 Feb 2005, Ivan Tomac wrote:
>>
>> Like Manfred's "map", it seems to be dup'ping the whole stack for each
>> thread.
>>
>
> No, it doesn't duplicate the whole stack. It only takes the number of
> elements required by each function and passes those elements to each
> function.
>
>> In fact what is the difference between t and map i?
>>
>
> They are completely different.
>
> t takes 1 parameter from the stack, a list of functions. It then finds
> out how many parameters each function inside the list requires for
> execution. It then applies each function/thread to corresponding
> arguments on stack (basically creates a new stack for each function
> that only contains the functions parameters).
>
> map on the other hand takes 2 parameters - a list and a function to
> iterate over the list and then composes a new list based on the
> results of the iterator function.
>
> 1 2 3 [[44 +] [55 +] [66 +]] t --> 45 57 69
> 1 2 3 [[44 +] [55 +] [66 +]] map i --> error
Sorry, my mistake, I was thinking of...
1 2 3 [[44 +] [55 +] [66 +]] [i] map.
[47 58 69]
In particular..
1 2 3 [[pop pop 44 +][pop 55 +][66 +]] [i] map.
[45 57 69]
The problem with 't' as you have defined it, it is literally
impossible to find out how many parameters an arbitrary fragment of Joy
requires.
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email :
john.carter@...
New Zealand
"The notes I handle no better than many pianists. But the pauses
between the notes -
ah, that is where the art resides!' - Artur Schnabel
Chris Double — 2005-02-11 03:14:30
On Fri, 11 Feb 2005 16:05:42 +1300 (NZDT), "John Carter"
<
john.carter@...> said:
>
> The problem with 't' as you have defined it, it is literally
> impossible to find out how many parameters an arbitrary fragment of Joy
> requires.
Factor does quite a good job using an 'infer' word:
[ + ] infer .
[ [ number number ] [ object ] ]
[ 20 + ] infer .
[ [ number ] [ object ] ]
Chris.
William Tanksley, Jr — 2005-02-11 03:16:21
Ivan Tomac <
e1_t@...> wrote:
> --- In concatenative@yahoogroups.com, "William Tanksley, Jr"
> <wtanksleyjr@g...> wrote:
> > On Thu, 10 Feb 2005 02:27:06 -0000, Ivan Tomac <e1_t@y...> wrote:
> > > Or better yet:
> > > abcn--abbbanc -> [[over dupd dupd] swap] t
> > I'm not sure what's happening here, but I admire the effect. What's
> > the t combinator?
> I've already described it in the following 2 posts:
Oh! I thought you meant "the T combinator", as though it were some
already-defined combinator. You actually meant "we can introduce a new
combinator, which I'll call 't'". That's cool.
> Initial goal was to implement some form of a simple but purely
> functional threading
> combinator (which is also why I decided to call it the t combinator)
> that would run a
> number of threads and and produce consistent results every time it's
> ran.
Okay, got it.
I don't see this as being a practical combinator given the current
nature of Joy. The problem is that Joy's stack effects aren't static
or even known to the compiler. But let's suppose Joy were changed so
that all stack effects were static and known.
Then we could (just for the sake of illustration) implement your 't'
combinator in terms of some other combinators -- we'd need dip, of
course, and a new combinator that determines the stack effect of a
quotation. The result wouldn't run stuff in parallel, but on a single
processor it would be indistinguishable from something that did.
> I haven't had enough time to think about that.
Perhaps there's an interesting result in there.
> Ivan
-Billy
John Carter — 2005-02-11 03:26:04
On Fri, 11 Feb 2005, Chris Double wrote:
>
> On Fri, 11 Feb 2005 16:05:42 +1300 (NZDT), "John Carter"
> <john.carter@...> said:
>>
>> The problem with 't' as you have defined it, it is literally
>> impossible to find out how many parameters an arbitrary fragment of Joy
>> requires.
>
> Factor does quite a good job using an 'infer' word:
>
> [ + ] infer .
> [ [ number number ] [ object ] ]
>
> [ 20 + ] infer .
> [ [ number ] [ object ] ]
It's that blooming i combinator that mucks it up totally.
i : [P] -> ...
Executes P. So, [P] i == P.
Totally basic to what Joy is, but what is it's stack effect? In the small
you can say, but in the large and general, you heading for Halting Problem
territory.
John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email :
john.carter@...
New Zealand
"The notes I handle no better than many pianists. But the pauses
between the notes -
ah, that is where the art resides!' - Artur Schnabel
Slava Pestov — 2005-02-11 03:29:15
John Carter wrote:
> It's that blooming i combinator that mucks it up totally.
>
> i : [P] -> ...
> Executes P. So, [P] i == P.
>
> Totally basic to what Joy is, but what is it's stack effect? In the small
> you can say, but in the large and general, you heading for Halting Problem
> territory.
There is no problem if the parameter to i is known.
[ [ + ] 3 swap call ] infer .
[ [ number ] [ object ] ]
Slava
Ivan Tomac — 2005-02-11 03:30:01
--- In
concatenative@yahoogroups.com, "William Tanksley, Jr"
<wtanksleyjr@g...> wrote:
> I don't see this as being a practical combinator given the current
> nature of Joy. The problem is that Joy's stack effects aren't static
> or even known to the compiler. But let's suppose Joy were changed so
> that all stack effects were static and known.
>
> Then we could (just for the sake of illustration) implement your 't'
> combinator in terms of some other combinators -- we'd need dip, of
> course, and a new combinator that determines the stack effect of a
> quotation. The result wouldn't run stuff in parallel, but on a single
> processor it would be indistinguishable from something that did.
>
I'm well aware that it can't be currently implemented in Joy but Joy
could be extended to support something like this.
As for the combinator not running the code in parallel - yes I'm aware
that in current implementation of Joy it wouldn't but there is no
reason why it couldn't be implemented in such a way. I've already
mentioned a way it could handle side-effects in that first post where
I described it.
Ivan
Ivan Tomac — 2005-02-11 03:54:42
--- In
concatenative@yahoogroups.com, Slava Pestov <slava@j...> wrote:
> John Carter wrote:
> > It's that blooming i combinator that mucks it up totally.
> >
> > i : [P] -> ...
> > Executes P. So, [P] i == P.
> >
> > Totally basic to what Joy is, but what is it's stack effect? In
the small
> > you can say, but in the large and general, you heading for Halting
Problem
> > territory.
>
> There is no problem if the parameter to i is known.
>
> [ [ + ] 3 swap call ] infer .
> [ [ number ] [ object ] ]
>
> Slava
You are both right.
Joy would have to be modified in a number of ways to support this.
One restriction may be that the number of elements on the stack
returned buy any single function is always the same.
Also it may be necessary to restrict the types for the then and else
clauses of the ifte combinator, so that both sides return the same
number of parameters.
There may be a way to relax those restrictions a bit though.
Currently, in Joy, construct could be used for the same purpose as t
but the programmer is required to manually remove the unwanted
elements which doesn't make it any more appealing then the
alternatives. On the other hand it could be rewritten to support
multi-threading as I've previously explained.
Ivan
William Tanksley, Jr — 2005-02-11 05:27:52
John Carter <
john.carter@...> wrote:
> Like Manfred's "map", it seems to be dup'ping the whole stack for each
> thread.
No, there's absolutely no 'dup' ing going on. The last quotation in
the list is given the top /n/ elements of the stack, where /n/ is
precisely the number of elements that it touches; each quotation in
the list is assigned its own stack elements.
> In fact what is the difference between t and map i?
't' has one additional concept that map i doesn't: it knows the stack
effects of quotations. I'm not sure that this is anything special or
valuable; it's definite that it's not something Joy has right at the
moment.
Wait, though!!!! I just thought of something. What if we replaced the
good old 'size' operator with a pair of operators, which I'll call
'inputs' and 'outputs'? Rather than counting the number of items
inside a list, these provide counts of the number of stack items the
given list would consume or generate when evaluated, respectively. For
lists of literals, the result of 'outputs' is the same as the old
result of 'size', and the result of 'inputs' is 0.
The interesting thing about this to me is that this change would make
it possible to apply rewriting rules within quotations.
Hmm, this isn't completely true. There's the problem of indexed access
into quotations. There's no way to index into the results of a
quotation, or even to get the first element of the quotation, without
evaluating it.
Shoot. That didn't work. And here I thought I found something clever.
It gets worse, of course: if the quotation contains an unbalanced ifte
or loop, the inputs and outputs can't be staticly computed.
Hmm. It doesn't look like the 't' combinator can be practically
implemented for Joy. A language that prohibited all dynamic stack
effects could do it.
Wait, I'm wrong. The 't' combinator could be implemented sequentially
by first running the last quotation in the list, then removing its
results (everything it touched on the stack -- there has to be SOME
way of figuring that out), repeating with the next-to-last, and so on.
When you finish evaluating the first quotation, you leave its results
on the stack, then place the second quotation's result back on the
stack, and so on. This won't work in parallel because there's no way
of knowing the stack effect of a quotation without running it first.
Now, I need to figure out how to figure out how to remove everything a
quotation touched from the stack. I think I have an answer. It's a
version of 'infra' which, when the quotation tries to access below the
depth of the temporary stack, items get (destructively) popped from
the real stack and placed on the temporary stack. I'll call this
combinator "into".
3 [4] [+] into ==> [7]
3 [4] [+] infra ==> 3 [7]
(I'm not running Joy right now to test this; this is what I recall of
Joy's behavior.)
This behavior would make 't' possible to implement, I think. I'm
getting some sleep before I embarass myself more in public, though.
-Billy
John Cowan — 2005-02-11 05:57:39
William Tanksley, Jr scripsit:
> Wait, I'm wrong. The 't' combinator could be implemented sequentially
> by first running the last quotation in the list, then removing its
> results (everything it touched on the stack -- there has to be SOME
> way of figuring that out), repeating with the next-to-last, and so on.
Because the Joy stack is implemented as a linked list of immutable stack
elements with garbage collection, it's trivial to restore the stack
to any state it ever had. Just copy the stack pointer into a local
variable in the implementation language; when you're done, restore the
stack pointer. Because the variable prevents the current contents of
the stack from becoming garbage, this always works. Many Joy combinators
(I don't remember which ones offhand) do exactly this.
--
So that's the tune they play on John Cowan
their fascist banjos, is it?
jcowan@...
--Great-Souled Sam
http://www.ccil.org/~cowan
Ivan Tomac — 2005-02-11 06:28:44
--- In
concatenative@yahoogroups.com, "William Tanksley, Jr"
<wtanksleyjr@g...> wrote:
> It gets worse, of course: if the quotation contains an unbalanced
ifte
> or loop, the inputs and outputs can't be staticly computed.
>
> Hmm. It doesn't look like the 't' combinator can be practically
> implemented for Joy. A language that prohibited all dynamic stack
> effects could do it.
>
> Wait, I'm wrong. The 't' combinator could be implemented
sequentially
> by first running the last quotation in the list, then removing its
> results (everything it touched on the stack -- there has to be SOME
> way of figuring that out), repeating with the next-to-last, and so
on.
> When you finish evaluating the first quotation, you leave its
results
> on the stack, then place the second quotation's result back on the
> stack, and so on. This won't work in parallel because there's no way
> of knowing the stack effect of a quotation without running it first.
>
> Now, I need to figure out how to figure out how to remove
everything a
> quotation touched from the stack. I think I have an answer. It's a
> version of 'infra' which, when the quotation tries to access below
the
> depth of the temporary stack, items get (destructively) popped from
> the real stack and placed on the temporary stack. I'll call this
> combinator "into".
>
Whether this can or can't be implemented in Joy isn't the point I was
making. By saying that we could add the t combinator to Joy I could
have implied that it would turn Joy into a new language - one that is
not Joy anymore. And that is exactly what I was talking about.
I used Joy as a template for this imaginary new language because Joy
already exists and because most people here are more or less familiar
with it.
Note that dynamic stack effects wouldn't have to be prohibited
completely. They only need to be prohibited inside the t combinator.
This doesn't exactly limit the expressiveness of the language as the
other sequential version of the combinator that doesn't worry about
dynamic stack effects could still be implemented.
In fact t could be implemented in such a way that in the abscence of
dynamic stack effects it runs the multithreaded version, or if they
are present runs the sequential version.
I've started to think that message boards like these are actually
pretty bad for discussing ideas. If anyone is interested I'd be quite
happy to continue the discussion on IRC, Freenode, #concatenative
channel. It is much easier to explain and discuss things in real time.
Ivan
William Tanksley, Jr — 2005-02-11 16:17:07
On Fri, 11 Feb 2005 03:30:01 -0000, Ivan Tomac <
e1_t@...> wrote:
> --- In concatenative@yahoogroups.com, "William Tanksley, Jr"
> <wtanksleyjr@g...> wrote:
> > I don't see this as being a practical combinator given the current
> > nature of Joy. The problem is that Joy's stack effects aren't static
> > or even known to the compiler. But let's suppose Joy were changed so
> > that all stack effects were static and known.
> > Then we could (just for the sake of illustration) implement your 't'
> > combinator in terms of some other combinators -- we'd need dip, of
> > course, and a new combinator that determines the stack effect of a
> > quotation. The result wouldn't run stuff in parallel, but on a single
> > processor it would be indistinguishable from something that did.
> I'm well aware that it can't be currently implemented in Joy but Joy
> could be extended to support something like this.
I was exploring how to do that, so yes, I agree.
> As for the combinator not running the code in parallel - yes I'm aware
> that in current implementation of Joy it wouldn't but there is no
> reason why it couldn't be implemented in such a way.
Yes, there is a reason why it can't be done -- the stack effects of a
quotation are not, in general, computable. This doesn't mean that it's
not a good combinator; it just means that it's not a good parallel
combinator.
> Ivan
-Billy
William Tanksley, Jr — 2005-02-11 16:33:51
John Cowan <
cowan@...> wrote:
> William Tanksley, Jr scripsit:
> > Wait, I'm wrong. The 't' combinator could be implemented sequentially
> > by first running the last quotation in the list, then removing its
> > results (everything it touched on the stack -- there has to be SOME
> > way of figuring that out), repeating with the next-to-last, and so on.
> Because the Joy stack is implemented as a linked list of immutable stack
> elements with garbage collection, it's trivial to restore the stack
> to any state it ever had. Just copy the stack pointer into a local
> variable in the implementation language; when you're done, restore the
> stack pointer. Because the variable prevents the current contents of
> the stack from becoming garbage, this always works. Many Joy combinators
> (I don't remember which ones offhand) do exactly this.
I've never liked this convention in Joy, but yes, I know about it.
After running the combinator, you have to perform a stack comparison,
which is wasteful and possibly misleading. I'd rather use
pop-on-demand.
> So that's the tune they play on John Cowan
-Billy
William Tanksley, Jr — 2005-02-11 16:43:21
Ivan Tomac <
e1_t@...> wrote:
>In concatenative@yahoogroups.com, "William Tanksley, Jr"
> <wtanksleyjr@g...> wrote:
> Whether this can or can't be implemented in Joy isn't the point I was
> making. By saying that we could add the t combinator to Joy I could
> have implied that it would turn Joy into a new language - one that is
> not Joy anymore. And that is exactly what I was talking about.
> I used Joy as a template for this imaginary new language because Joy
> already exists and because most people here are more or less familiar
> with it.
I'm sorry I misunderstood; I thought you were trying to propose a new
combinator, not a new language. But I think my explorations have some
value; what I've said is true.
> Note that dynamic stack effects wouldn't have to be prohibited
> completely. They only need to be prohibited inside the t combinator.
In practice, this is extremely difficult. I've tried. Once you lose
static track of the stack depth, it's very hard to prove whether or
not you've got it back in control. But Slava may have some better
solutions for this problem.
> This doesn't exactly limit the expressiveness of the language as the
> other sequential version of the combinator that doesn't worry about
> dynamic stack effects could still be implemented.
> In fact t could be implemented in such a way that in the abscence of
> dynamic stack effects it runs the multithreaded version, or if they
> are present runs the sequential version.
That might be interesting, if possible. I'm more interested in whether
it's possible to build an expressive, useful language without dynamic
stack effects.
> Ivan
-Billy
sa@dfa.com — 2005-02-11 17:18:18
"William Tanksley, Jr" <
wtanksleyjr@...> wrote on 02/11/2005 11:43:21
AM:
>
> That might be interesting, if possible. I'm more interested in whether
> it's possible to build an expressive, useful language without dynamic
> stack effects.
me too
Ehrenberg Daniel — 2005-02-14 03:06:10
> That might be interesting, if possible. I'm more interested in whether
> it's possible to build an expressive, useful language without dynamic
> stack effects.
It's very possible. Keep in mind that pretty much all nonconcatenative
languages, even ones with variable arguments (not counting C) have, in
effect, statically known stack effects. Concatenative languages aren't
the only expressive, useful languages. Most combinators can be
formulated with statically known (just complicated) stack effects, as
long as you accept that quotations have statically know effects. This,
in a way, is what the compilable subset of Factor already does.
Daniel Ehrenberg
phimvt@lurac.latrobe.edu.au — 2005-02-14 06:08:10
On Wed, 9 Feb 2005 phimvt@... wrote:
[..]
See end of the email for explanatory web page.
> DEFINE
> quadratic-2 == # a b c => [root1 root2 ]
> [ [ [ pop pop 2 * ] # divisor
> [ pop 0 swap - ] # minusb
> [ swap dup * rollup * 4 * - sqrt ] ] # radical
> [i] map ]
> ternary i
> [ [ [ + swap / ] # root1
> [ - swap / ] ] # root2
> [i] map ]
> ternary.
It occurred to me that stack shuffling operations can be written
using the combination [i] map i. For example:
DEFINE abc--abcaabbcc ==
[[pop pop] [pop pop] [pop] [pop] [] []] [i] map i
If the result sequence is not to include the source sequence,
then any deletion best occurs between the map and the final i.
[ a propos the explanatory web page not being visible: ]
> I conjecture that our systems
> administrators are now acting as censors to weed out undesirable material,
> and that the censor is overworked.
A little bit of paranoia goes a long way - even all the way around the
earth, on the wings of google and concatenative (he waxes lyrically).
It so turned out that I was working on the "old" web machine, and all
stuff had been copied to the "new" web machine. I never noticed.
Anyhow, the explanatory web page about the quadratic is now visible
from the main Joy page or directly from
http://www.latrobe.edu.au/philosophy/phimvt/joy/jp-quadratic.html
- Manfred
phimvt@lurac.latrobe.edu.au — 2005-02-14 06:19:39
On Wed, 9 Feb 2005, John Carter wrote:
>
> On Wed, 9 Feb 2005 phimvt@... wrote:
[..]
> > DEFINE
> > quadratic-2 == # a b c => [root1 root2 ]
> > [ [ [ pop pop 2 * ] # divisor
> > [ pop 0 swap - ] # minusb
> > [ swap dup * rollup * 4 * - sqrt ] ] # radical
> > [i] map ]
> > ternary i
> > [ [ [ + swap / ] # root1
> > [ - swap / ] ] # root2
> > [i] map ]
> > ternary.
>
>
> Ok, so (and I have checked it out on Joy)...
> [ [ pop pop 2 * ] # divisor
> [ pop 0 swap - ] # minusb
> [ swap dup * rollup * 4 * - sqrt ] ] # radical
> [i] map
> ...works.
>
> But given the description...
> map : A [P] -> B
> Executes P on each member of aggregate A, collects results in
> same type aggregate B.
> ...I don't see how it works.
>
> Does map dup the whole stack before each and every execution of P?
Not a dup really, but it save a single pointer to the "old" stack
(easy and cheap if the stack is a linked list), and uses that
pointer to restore the old stack for each and every execution of P
(also easy and cheap). (Much harder if the stack is an array - but a
complete copy should never be necessary.)
Many other combinators use the same technique (think of ifte, which
has to restore the old stack after the if-part has been executed).
Hope this helps.
- Manfred
phimvt@lurac.latrobe.edu.au — 2005-02-14 06:52:45
On Fri, 11 Feb 2005, John Carter wrote:
> On Fri, 11 Feb 2005, Ivan Tomac wrote:
> > map on the other hand takes 2 parameters - a list and a function to
> > iterate over the list and then composes a new list based on the
> > results of the iterator function.
> >
> > 1 2 3 [[44 +] [55 +] [66 +]] t --> 45 57 69
> > 1 2 3 [[44 +] [55 +] [66 +]] map i --> error
>
> Sorry, my mistake, I was thinking of...
>
> 1 2 3 [[44 +] [55 +] [66 +]] [i] map.
> [47 58 69]
And in Joy the original 1 2 3 are still on the stack, just below
the result list.
> In particular..
> 1 2 3 [[pop pop 44 +][pop 55 +][66 +]] [i] map.
> [45 57 69]
Ditto. In both cases this is because map only consumes two parameters, a
function (here [i], whichh happens to be a combinator) and a list (here
the three element lists which happen to be quotations in both cases). But
there is nothing to stop the quotations from looking deeper into the stack
- indeed to forbid it would be to cripple Joy.
- Manfred
John Cowan — 2005-02-15 15:24:02
William Tanksley, Jr — 2005-02-15 18:50:55
On Tue, 15 Feb 2005 10:24:02 -0500, John Cowan <
cowan@...> wrote:
> William Tanksley, Jr scripsit:
> > I've never liked this convention in Joy, but yes, I know about it.
> > After running the combinator, you have to perform a stack comparison,
> > which is wasteful and possibly misleading.
> Can you explain this, please? I don't know what a "stack comparison" is.
It's where you try to figure out what's changed in a stack by
comparing each element.
> Some people open all the Windows; John Cowan
-Billy
Ehrenberg Daniel — 2005-02-25 05:19:18
You guys thought this thread was dead, didn't you?
With a lot of help from slava and eiz from #concatenative, I've
created an infix minilanguage. Using this language, it's trivial to
make the quadratic formula. It may not be the most concatenative way,
but I think it's less awkward than the solutions I've seen so far.
Here it is:
: quadratic-formula (| a b c |
[ [ - b ] / 2 * a ] +- [ sqrt [ sq b ] - 4 * a * c ] / 2 * a |) ;
The precedence, to everyone's dismay, is all equal, and it's right
associative. +- is defined as : +- [ + ] 2keep - ;, and since it
returns multiple results, it can only safely be used at the top level
of the expression. Internally, this translates to a mess of vector
creation and referencing, replacing the variables and compiling at
parsetime. For speed, I will eventually switch the bounds-checked
vector with a non-bounds-checked array, but it already seems pretty
efficient: for adding 2 numbers, it took 3 microseconds, compared with
1 microsecond for the primitive operation, all on my extremely slow
200 mhz computer. The general syntax is (| variable names | infix
expression|). The variable values are--or at least it seems to the
user as if they are--substituted in at runtime for the values in the
corresponding position on the stack. For example, 1 2 (| x y | x + y
|) is effectively equivalent to 1 2 +. I know local variables hurt
factoring, but I think in this case it's justafiable, since the
alternative all too often is worse. Possibly a similar strategy could
be used for a more general implementation of local variables, but that
would just be evil. In addition to infix evaluation, there is (or
rather there will be) somewhat of a stripped down CAS. Right now, it
can't really do that much.
So, Joy users, try to make a combinator for all that!
Daniel Ehrenberg
William Tanksley, Jr — 2005-02-25 18:35:02
Ehrenberg Daniel <
microdan@...> wrote:
> With a lot of help from slava and eiz from #concatenative, I've
> created an infix minilanguage. Using this language, it's trivial to
> make the quadratic formula. It may not be the most concatenative way,
> but I think it's less awkward than the solutions I've seen so far.
Excellent -- that problem's solved.
Now, on to the quintic formula! :-)
Seriously, though, that's a good approach. To make it a great
approach, specialise your language even more for numerical
computations. In other words, don't just do a naive translation or a
simple optimization; actually make sure the code you generate is
reasonably numerically stable.
I believe that one of the strengths of concatenative languages is the
fact that they don't have to be parsed; this means that you can build
special-purpose microlanguages like this, and the compiler won't ever
notice.
Concatenative theory doesn't cover such microlanguages, nor does it
yet cover any functions that modify the source like that -- but I
think the latter part might be possible.
> Daniel Ehrenberg
-Billy
Slava Pestov — 2005-02-25 20:56:41
Dan,
This is a very nice piece of work, and it indeed it shows how easy it is
to extend Factor to fit a problem. You forgot to mention that the code
can be found in CVS at
http://cvs.sourceforge.net/viewcvs.py/factor/Factor/Factor/contrib/algebra/
and will be part of the next Factor release.
Ehrenberg Daniel wrote:
> You guys thought this thread was dead, didn't you?
>
> With a lot of help from slava and eiz from #concatenative, I've
> created an infix minilanguage. Using this language, it's trivial to
> make the quadratic formula. It may not be the most concatenative way,
> but I think it's less awkward than the solutions I've seen so far.
> Here it is:
>
> : quadratic-formula (| a b c |
> [ [ - b ] / 2 * a ] +- [ sqrt [ sq b ] - 4 * a * c ] / 2 * a |) ;
>
> The precedence, to everyone's dismay, is all equal, and it's right
> associative. +- is defined as : +- [ + ] 2keep - ;, and since it
> returns multiple results, it can only safely be used at the top level
> of the expression. Internally, this translates to a mess of vector
> creation and referencing, replacing the variables and compiling at
> parsetime. For speed, I will eventually switch the bounds-checked
> vector with a non-bounds-checked array, but it already seems pretty
> efficient: for adding 2 numbers, it took 3 microseconds, compared with
> 1 microsecond for the primitive operation, all on my extremely slow
> 200 mhz computer. The general syntax is (| variable names | infix
> expression|). The variable values are--or at least it seems to the
> user as if they are--substituted in at runtime for the values in the
> corresponding position on the stack. For example, 1 2 (| x y | x + y
> |) is effectively equivalent to 1 2 +. I know local variables hurt
> factoring, but I think in this case it's justafiable, since the
> alternative all too often is worse. Possibly a similar strategy could
> be used for a more general implementation of local variables, but that
> would just be evil. In addition to infix evaluation, there is (or
> rather there will be) somewhat of a stripped down CAS. Right now, it
> can't really do that much.
>
> So, Joy users, try to make a combinator for all that!
>
> Daniel Ehrenberg
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
>
stevan apter — 2005-02-25 22:37:46
----- Original Message -----
From: "William Tanksley, Jr" <wtanksleyjr@...>
To: <concatenative@yahoogroups.com>
Sent: Friday, February 25, 2005 1:35 PM
Subject: Re: [stack] The annoying quadratic formul
>
> Ehrenberg Daniel <microdan@...> wrote:
> > With a lot of help from slava and eiz from #concatenative, I've
> > created an infix minilanguage. Using this language, it's trivial to
> > make the quadratic formula. It may not be the most concatenative way,
> > but I think it's less awkward than the solutions I've seen so far.
>
> Excellent -- that problem's solved.
>
> Now, on to the quintic formula! :-)
>
> Seriously, though, that's a good approach.
i guess i'm left wondering why it isn't a good approach
for non-numeric problems. :)
Ehrenberg Daniel — 2005-02-28 22:41:28
> Excellent -- that problem's solved.
I'm not sure it's that simple. I'm not very happy with the fact that
we had to resort to variables for this.
>
> Now, on to the quintic formula! :-)
>
> Seriously, though, that's a good approach. To make it a great
> approach, specialise your language even more for numerical
> computations. In other words, don't just do a naive translation or a
> simple optimization; actually make sure the code you generate is
> reasonably numerically stable.
>
What do you mean? So far, my only optimization is constant folding,
and I don't plan to add more, except maybe a more efficient
representation of the structure holding the numbers.
> I believe that one of the strengths of concatenative languages is the
> fact that they don't have to be parsed; this means that you can build
> special-purpose microlanguages like this, and the compiler won't ever
> notice.
>
What are you talking about? A key part of the minilanguage is the
parser, which is implemented by extending Factor's parser with a
macro, basically. All languages need to be parsed except machine code.
The parsing may be simple, but it's still there. I'm happy to say that
the parser for infix is a small part of the code. This all would be
equally possible (and actually much easier, since it already has
variables) in Lisp.
> Concatenative theory doesn't cover such microlanguages, nor does it
> yet cover any functions that modify the source like that -- but I
> think the latter part might be possible.
>
> -Billy
I don't really care about concatenative theory, but maybe you could
analyze the purely concatenative result of compilation. But the source
code is applicative, not concatenative.
Daniel Ehrenberg
Ehrenberg Daniel — 2005-02-28 22:46:04
> i guess i'm left wondering why it isn't a good approach
> for non-numeric problems. :)
Because it destroys everything we've gained from concatenative
languages, most importantly (for me) cut-and-paste factoring. You have
to rename variables when factoring. It also makes multiple results
much less convienent: currently, a function returning 2 things should
be the outermost operation, otherwise there will be a runtime type
error since the array holding the data will be too low on the stack. I
haven't found any suitable solution to this problem so far.
Daniel Ehrenberg
stevan apter — 2005-03-01 11:34:51
----- Original Message -----
From: "Ehrenberg Daniel" <microdan@...>
To: <concatenative@yahoogroups.com>
Sent: Monday, February 28, 2005 5:46 PM
Subject: Re: [stack] The annoying quadratic formul
>
> > i guess i'm left wondering why it isn't a good approach
> > for non-numeric problems. :)
>
> Because it destroys everything we've gained from concatenative
> languages, most importantly (for me) cut-and-paste factoring. You have
> to rename variables when factoring.
variable renaming is an inconvenience to be sure, but as someone
once said (perhaps it was me?), a coding inconvenience is merely
an opportunity to automate. so suppose we have an expression e in
variables a, b, c with subsequences X, Y, and Z (i'll use XY
notation here - ^ is concatenation):
; e { [a b c] X^Y^Z } ;
and you want to factor out Y (perhaps because you've noticed that
Y occurs in other places in your code, possibly using variables
other than a, b, c.) so you want some operation, 'factor', which
takes e and Y and returns e' and Y' with Y factored out:
; e' { [a b c] X^W^Z } ;
; Y' .. ;
'factor' has to compute W, which sets up the stack for Y':
.. Y'
and it has to compute Y', which is an expression in no more than
three variables.
smart cut-and-paste factoring would use 'factor'.
this seems eminently do-able, no more than you'd expect to need
given the extra expressive power delivered by variables.
It also makes multiple results
> much less convienent: currently, a function returning 2 things should
> be the outermost operation, otherwise there will be a runtime type
> error since the array holding the data will be too low on the stack. I
> haven't found any suitable solution to this problem so far.
this problem doesn't arise in a vector concatenative language.
>
> Daniel Ehrenberg
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
sa@dfa.com — 2005-03-01 14:55:02
"stevan apter" <
sa@...> wrote on 03/01/2005 06:34:51 AM:
>
>
> ----- Original Message -----
> From: "Ehrenberg Daniel" <microdan@...>
> To: <concatenative@yahoogroups.com>
> Sent: Monday, February 28, 2005 5:46 PM
> Subject: Re: [stack] The annoying quadratic formul
>
>
> >
> > > i guess i'm left wondering why it isn't a good approach
> > > for non-numeric problems. :)
> >
> > Because it destroys everything we've gained from concatenative
> > languages, most importantly (for me) cut-and-paste factoring. You have
> > to rename variables when factoring.
>
> variable renaming is an inconvenience to be sure, but as someone
> once said (perhaps it was me?), a coding inconvenience is merely
> an opportunity to automate. so suppose we have an expression e in
> variables a, b, c with subsequences X, Y, and Z (i'll use XY
> notation here - ^ is concatenation):
>
> ; e { [a b c] X^Y^Z } ;
>
> and you want to factor out Y (perhaps because you've noticed that
> Y occurs in other places in your code, possibly using variables
> other than a, b, c.) so you want some operation, 'factor', which
> takes e and Y and returns e' and Y' with Y factored out:
>
> ; e' { [a b c] X^W^Z } ;
> ; Y' .. ;
>
> 'factor' has to compute W, which sets up the stack for Y':
>
> .. Y'
>
> and it has to compute Y', which is an expression in no more than
> three variables.
>
> smart cut-and-paste factoring would use 'factor'.
>
> this seems eminently do-able, no more than you'd expect to need
> given the extra expressive power delivered by variables.
it's easier than i thought. consider
; +/- [1 -1] * + ;
; quad { [a b c] b -: a c * 4 * b 2 ^ -. _sqrt +/- a 2 * % } ;
---------------
the quadratic formula in XY. suppose you want to factor out
the underlined segment a c * 4 * b 2 ^. first, find the variables
in that segment:
a c b
create:
; foo { [a c b] a c * 4 * b 2 ^ } ;
no renaming is necessary. then replace the segment in quad by
prefixing the variables (in the same order) to a call to foo:
; quad { [a b c] b -: a c b foo -. _sqrt +/- a 2 * % } ;
a little free-associative notation-design:
we know that uniform renaming preserves meaning - foo and
; goo { [x y z] x y * 4 * z 2 ^ } ;
mean the same. so imagine a notation -- a 2-d notation --
in which we abstract pure positional information from the list
of variables, and from their occurrences in the code. we
replace a variable list like [x y z] with a dot-list:
[ . . . ]
and lines are drawn from each dot to positions in the code
where the corresponding stack-element should occur:
[ . . . ] . . * 4 * . 2 ^
| | | | | |
| | |---+-+-------|
| |-----+-|
|-------|
when you cut from such a diagram, the code parts are lifted
and moved, and the lines are stretched, moving along with
the code. when the cut terminates, the dots and lines in
the original snap to the left and leave a place _ for the
call to the cut:
[ . . . ] . . . _ 2 ^
| | | | | |
| | |---+-+-|
| |-----+-|
|-------|
dots in the body of the cut "grow" to connect with a new
dot-list:
[ . . . ] . . * 4 .
| | | | | |
| | |---+-+-----|
| |-----+-|
|-------|
alternatively, you can imagine that the intermediate state,
in which the dots in the child are connected directly to the
dot-list in the parent. that is, the child is just
. . * 4 .
and its dots are connected to the dot-list of the parent. in
this notation, a child can be connected to multiple parents by
having multiple lines proceed from each of its dots, terminating
in the dot-lists of the separate parents.
the dot/line notation gives us a positional method for directly
manipulating stack-elements. we can also imagine a graphical
mechanism for program-calls, which would eliminate the need
to name programs. a program begins with some connector, say _:
_ [ . . . ] . . * 4 .
one program calls another by containing a _ with a special
style of line connecting its _ with the _ at the head of
the called program.
i don't think it would be terribly difficult to write an editor
for the graphical notation i've been describing.
> It also makes multiple results
> > much less convienent: currently, a function returning 2 things should
> > be the outermost operation, otherwise there will be a runtime type
> > error since the array holding the data will be too low on the stack. I
> > haven't found any suitable solution to this problem so far.
>
> this problem doesn't arise in a vector concatenative language.
>
> >
> > Daniel Ehrenberg
> >
> >
> >
> > Yahoo! Groups Links
> >
> >
> >
> >
> >
> >
> >
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
William Tanksley, Jr — 2005-03-01 17:16:06
On Mon, 28 Feb 2005 17:41:28 -0500, Ehrenberg Daniel <
microdan@...> wrote:
> > Excellent -- that problem's solved.
> I'm not sure it's that simple. I'm not very happy with the fact that
> we had to resort to variables for this.
An ideal program speaks the same language as the experts who know how
to solve the problem at hand. If your problem is mathematical, you
want to speak the language mathematicians use.
The language mathematicians typically use is a clumsy, difficult
language to use to express programs; but it's well tuned to do its
real job.
However, it's not perfectly clear to me that using mathematical
notation is the ultimate answer for this problem. If the problem was
expressing arbitrary mathematical formulas, I would suggest this
parser, augmented by an optimizer that generated numerically stable
code; however, if the problem was simply to solve the quadratic
equation (or any other single formula), I would just code it
procedurally, attempting to factor nicely; I would clearly show the
"how" and trust my names and comments to convey what exactly I was
computing the value of.
Yes, it would take a few more characters; but experience has shown
that on average, I take fewer characters to express programs, so I
don't need to pinch pennies when dealing with this part of a program.
I know that's a weaseling-out, and my theoretician personality hates
me for it. But practically, until the theoretician gives an answer for
how to solve problems like this cleanly in a concatenative language, I
have only two choices: I can weasel out by switching to an applicative
language, or I can weasel out by switching to a procedural style
that's more verbose. Either way I'm a weasel. Remember: eagles may
soar, but weasels don't get sucked into jet engines.
> > Seriously, though, that's a good approach. To make it a great
> > approach, specialise your language even more for numerical
> > computations. In other words, don't just do a naive translation or a
> > simple optimization; actually make sure the code you generate is
> > reasonably numerically stable.
> What do you mean? So far, my only optimization is constant folding,
> and I don't plan to add more, except maybe a more efficient
> representation of the structure holding the numbers.
I was explaining what you'd need if you wanted to make this generally
useful for what you built it for. Without numeric stability, a
mathematical formula syntax is just a way of lying to mathematicians
-- you're making it look like your code was trying to compute a
formula the mathmaticians recognise, but it really doesn't.
> > I believe that one of the strengths of concatenative languages is the
> > fact that they don't have to be parsed; this means that you can build
> > special-purpose microlanguages like this, and the compiler won't ever
> > notice.
> What are you talking about? A key part of the minilanguage is the
> parser, which is implemented by extending Factor's parser with a
> macro, basically. All languages need to be parsed except machine code.
Not true. Although some concatenative languages are parsed, not all
need to be. Formally speaking, a concatenative language needs only to
be tokenized, and each token mapped onto its semantics. That's it; no
parse stage, no parse tree, no lookahead of any kind. Every word is
executed (or compiled) immediately as the compiler hits it.
> The parsing may be simple, but it's still there. I'm happy to say that
> the parser for infix is a small part of the code. This all would be
> equally possible (and actually much easier, since it already has
> variables) in Lisp.
The designers of Lisp thought ahead -- they provided a way to
short-circuit the parser or even the reader temporarily. It requires a
special construct, though, in order to tell the parser "ignore the
following...".
> > Concatenative theory doesn't cover such microlanguages, nor does it
> > yet cover any functions that modify the source like that -- but I
> > think the latter part might be possible.
> I don't really care about concatenative theory, but maybe you could
> analyze the purely concatenative result of compilation.
You're definitely in the wrong discussion group to not care about
concatenative theory... :-)
I suspect that I might know what you're saying, and respect that to
get real work done, you have to disregard the inadequacies of theory
and just move on to what works. But when I put on my theoretician's
hat, I have to ask WHY the theory doesn't work, and whether it might
eventually be possible to make it do so. Hence my above statement.
> But the source code is applicative, not concatenative.
You mean the source code in your microlanguage is applicative. Yes.
But it's nested within a concatenative language.
> Daniel Ehrenberg
-Billy
William Tanksley, Jr — 2005-03-01 17:38:32
stevan apter <
sa@...> wrote:
> From: "William Tanksley, Jr" <wtanksleyjr@...>
> > Ehrenberg Daniel <microdan@...> wrote:
> > > With a lot of help from slava and eiz from #concatenative, I've
> > > created an infix minilanguage. Using this language, it's trivial to
> > > make the quadratic formula. It may not be the most concatenative way,
> > > but I think it's less awkward than the solutions I've seen so far.
> > Seriously, though, that's a good approach.
> i guess i'm left wondering why it isn't a good approach
> for non-numeric problems. :)
Heh. :-)
Writing a customized language is a good approach for every problem.
It's the core of working with Forth; every decent-sized Forth
application mostly consists of creating a vocabulary and grammar that
fit the vocabulary and grammar of the actual problem.
There's some critical things to watch out for while you're doing that, though.
First, is to not get too ambitious; don't write more code than you
have to. Don't waste your customer's time with code that does nothing
more than make itself look pretty. It's really just a hiding place for
bugs.
Second, stay within the language. Don't switch back and forth between
infix, suffix, and prefix. Even if this means that the language of the
problem domain will have to be slightly bastardised, it's worth it to
create a clear tie between the implementation language and the
solution.
Finally, a warning of my own: if you're used to infix and applicative,
but you want to learn Forth or some other concatenative language,
these infix extensions will naturally be highly tempting. They may
appear more powerful. No... only quicker, easier, more seductive...
Easily they flow, quick to join you. If once you start down the dark
path, forever will it dominate your destiny, consume you it will.
When the wise man learns the Way, He tries to live by it.
When the average man learns the Way, he lives by only part of it.
When the fool learns the Way, He laughs at it.
Yet if the fool did not laugh at it, It would not be the Way.
Indeed, if you are seeking the Way, Listen for the laughter of fools.
Seriously, concatenative languages are not the Way. :-) But I do like
that quote. A good programmer will choose the right tool for the job,
and a good language designer will design a tool that's as flexible as
possible, without giving up the fundamental purpose of the job.
But in this group we're all learning, and our first question shouldn't
be "how do I get around this difficulty the quickest," but rather
"what can I learn from this difficulty?"
-Billy
John Cowan — 2005-03-01 18:03:30
William Tanksley, Jr scripsit:
> When the wise man learns the Way, He tries to live by it.
> When the average man learns the Way, he lives by only part of it.
> When the fool learns the Way, He laughs at it.
> Yet if the fool did not laugh at it, It would not be the Way.
> Indeed, if you are seeking the Way, Listen for the laughter of fools.
Thoughtful hackers hear about Unix
and try to use it.
Ordinary hackers hear about Unix
and mess about with it a little.
Thoughtless hackers hear about Unix
and crack wise about it.
It wouldn't be Unix
if there weren't wisecracks about it.
So we establish the following rules:
The most brilliant Unix seems the most obscure.
Advanced Unix seems like retrocomputing.
The most powerful code seems like just loops and conditionals.
The clearest code seems to be opaque.
The sharpest tools seem inadequate.
Solid code seems flaky.
Stable code seems to change.
Great methodologies don't have boundaries.
Great talent doesn't code fast.
Great music makes no sound.
The ideal elephant has no shape.
The Unix Way has no name.
Yet for just this reason
it brings things to perfection.
--
http://www.ccil.org/upc, verse 41
--
"Clear? Huh! Why a four-year-old child John Cowan
could understand this report. Run out
jcowan@...
and find me a four-year-old child. I
http://www.ccil.org/~cowan
can't make head or tail out of it."
http://www.reutershealth.com
--Rufus T. Firefly on government reports
John Cowan — 2005-03-01 18:08:43
sa@dfa.com — 2005-03-01 18:53:15
i have, on occasion, found it useful to implement a
sublanguage as part of an application, but in every
such instance the rationale was that that part of
the application involved a small number of operations
performed over a small set of objects. the reduced
expressive demands in turn permitted a corresponding
increase in simplicity of grammar and term-formation.
the sublanguage needed to deal with fewer cases than
the superlanguage.
in every such case, a judgement was required: is the
resulting simplification sufficient to offset the increased
architectural complexity?
the point of my question was that applicative/infix
doesn't really merit the term "sublanguage". it is fully
as general, and just as expressive, as the concatenative/
postfix language in which it is embedded.
the consensus (tentative in some cases) is that infix/
applicative is better for math. so what is concatenative/
postfix better for, that it should be in the "super"
position? and what characteristics of "math" make the
infix/applicative approach "better"?
"William Tanksley, Jr" <
wtanksleyjr@...> wrote on 03/01/2005 12:38:32
PM:
>
> stevan apter <sa@...> wrote:
> > From: "William Tanksley, Jr" <wtanksleyjr@...>
> > > Ehrenberg Daniel <microdan@...> wrote:
> > > > With a lot of help from slava and eiz from #concatenative, I've
> > > > created an infix minilanguage. Using this language, it's trivial to
> > > > make the quadratic formula. It may not be the most concatenative
way,
> > > > but I think it's less awkward than the solutions I've seen so far.
> > > Seriously, though, that's a good approach.
>
> > i guess i'm left wondering why it isn't a good approach
> > for non-numeric problems. :)
>
> Heh. :-)
>
> Writing a customized language is a good approach for every problem.
> It's the core of working with Forth; every decent-sized Forth
> application mostly consists of creating a vocabulary and grammar that
> fit the vocabulary and grammar of the actual problem.
>
> There's some critical things to watch out for while you're doing that,
though.
>
> First, is to not get too ambitious; don't write more code than you
> have to. Don't waste your customer's time with code that does nothing
> more than make itself look pretty. It's really just a hiding place for
> bugs.
>
> Second, stay within the language. Don't switch back and forth between
> infix, suffix, and prefix. Even if this means that the language of the
> problem domain will have to be slightly bastardised, it's worth it to
> create a clear tie between the implementation language and the
> solution.
>
> Finally, a warning of my own: if you're used to infix and applicative,
> but you want to learn Forth or some other concatenative language,
> these infix extensions will naturally be highly tempting. They may
> appear more powerful. No... only quicker, easier, more seductive...
> Easily they flow, quick to join you. If once you start down the dark
> path, forever will it dominate your destiny, consume you it will.
>
> When the wise man learns the Way, He tries to live by it.
> When the average man learns the Way, he lives by only part of it.
> When the fool learns the Way, He laughs at it.
> Yet if the fool did not laugh at it, It would not be the Way.
> Indeed, if you are seeking the Way, Listen for the laughter of fools.
>
> Seriously, concatenative languages are not the Way. :-) But I do like
> that quote. A good programmer will choose the right tool for the job,
> and a good language designer will design a tool that's as flexible as
> possible, without giving up the fundamental purpose of the job.
>
> But in this group we're all learning, and our first question shouldn't
> be "how do I get around this difficulty the quickest," but rather
> "what can I learn from this difficulty?"
>
> -Billy
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
Ehrenberg Daniel — 2005-03-01 19:39:46
> An ideal program speaks the same language as the experts who know how
> to solve the problem at hand. If your problem is mathematical, you
> want to speak the language mathematicians use.
>
> The language mathematicians typically use is a clumsy, difficult
> language to use to express programs; but it's well tuned to do its
> real job.
>
> However, it's not perfectly clear to me that using mathematical
> notation is the ultimate answer for this problem. If the problem was
> expressing arbitrary mathematical formulas, I would suggest this
> parser, augmented by an optimizer that generated numerically stable
> code; however, if the problem was simply to solve the quadratic
> equation (or any other single formula), I would just code it
> procedurally, attempting to factor nicely; I would clearly show the
> "how" and trust my names and comments to convey what exactly I was
> computing the value of.
>
> Yes, it would take a few more characters; but experience has shown
> that on average, I take fewer characters to express programs, so I
> don't need to pinch pennies when dealing with this part of a program.
>
> I know that's a weaseling-out, and my theoretician personality hates
> me for it. But practically, until the theoretician gives an answer for
> how to solve problems like this cleanly in a concatenative language, I
> have only two choices: I can weasel out by switching to an applicative
> language, or I can weasel out by switching to a procedural style
> that's more verbose. Either way I'm a weasel. Remember: eagles may
> soar, but weasels don't get sucked into jet engines.
>
We can't make a minilanguage for every domain. If we're making some
sort of grammar checker, do you expect people to be able to program it
in English?
> I was explaining what you'd need if you wanted to make this generally
> useful for what you built it for. Without numeric stability, a
> mathematical formula syntax is just a way of lying to mathematicians
> -- you're making it look like your code was trying to compute a
> formula the mathmaticians recognise, but it really doesn't.
What is numeric stability?
>
> Not true. Although some concatenative languages are parsed, not all
> need to be. Formally speaking, a concatenative language needs only to
> be tokenized, and each token mapped onto its semantics. That's it; no
> parse stage, no parse tree, no lookahead of any kind. Every word is
> executed (or compiled) immediately as the compiler hits it.
>
What's the big difference between tolkenization and parsing? If a
language has single-pass parsing and tolkenization in one, it's still
being parsed. Factor is this way.
> The designers of Lisp thought ahead -- they provided a way to
> short-circuit the parser or even the reader temporarily. It requires a
> special construct, though, in order to tell the parser "ignore the
> following...".
Factor has a special way of defining words that interact with the
parser (or tolkenizer, if you want to call it that). I use these words
in my math thing.
> You're definitely in the wrong discussion group to not care about
> concatenative theory... :-)
>
> I suspect that I might know what you're saying, and respect that to
> get real work done, you have to disregard the inadequacies of theory
> and just move on to what works. But when I put on my theoretician's
> hat, I have to ask WHY the theory doesn't work, and whether it might
> eventually be possible to make it do so. Hence my above statement.
>
> > But the source code is applicative, not concatenative.
>
> You mean the source code in your microlanguage is applicative. Yes.
> But it's nested within a concatenative language.
I only care about concatenative theory inasmuch as it helps you
actually program. Concatenative theory, or any language theory, works
best on a minimal, unuseful subset of a language, I think.
Daniel Ehrenberg
William Tanksley, Jr — 2005-03-01 19:46:43
sa@... <
sa@...> wrote:
> i have, on occasion, found it useful to implement a
> sublanguage as part of an application, but in every
> in every such case, a judgement was required: is the
> resulting simplification sufficient to offset the increased
> architectural complexity?
That wouldn't be my question; my question would be whether the
sublanguage was closer to the solution domain. I'm not saying that you
have to ask the same questions I do :-), I'm just telling you what
Forth programmers (who are supposed to write new languages for every
problem they solve) ask themselves.
> the point of my question was that applicative/infix
> doesn't really merit the term "sublanguage". it is fully
> as general, and just as expressive, as the concatenative/
> postfix language in which it is embedded.
Sublanguage wasn't intended to imply a lesser language, but rather a
different language that runs under the first language.
> the consensus (tentative in some cases) is that infix/
> applicative is better for math.
> what characteristics of "math" make the
> infix/applicative approach "better"?
No, it's better for communicating with many mathematicians. A more
procedural notation would work better for numeric computation experts;
for that you'd be wasting your time with infix.
> so what is concatenative/postfix better for, that it should
> be in the "super" position?
An excellent question, even with the misunderstanding of my word
"sublanguage". I take your question to mean "why should I ever code in
a concatenative language, and why should I write customised languages
in one, rather than picking from a large toolbox of languages?"
I can come up with a partial list of answers.
1. Because it's fun. I've experientially proven this to myself; Forth
is a joy to work with, especially after months in the desert of C. I
suspect it's a consequence of the following:
2. Because it's theoretically consistent without being practically
limiting. The theoretically clean operations in concatenative
languages remain theoretically clean even after you've done convenient
things like modify global variables and such.
3. Because it's easy to create new languages in a good concatenative
language. The base language isn't complex, and doesn't force any kind
of lookahead, so you can do whatever you need in your sublanguage --
whether it only involves using odd characters in names, or it involves
creating a parser that consumes source code and hides it from the
regular compiler.
4. Because if you choose to create sublanguages rather than switching
to entirely different languages, the core of your work will be
compatible; you'll be able to borrow freely.
-Billy
William Tanksley, Jr — 2005-03-01 20:16:26
Ehrenberg Daniel <
microdan@...> wrote:
> > An ideal program speaks the same language as the experts who know how
> > to solve the problem at hand. If your problem is mathematical, you
> > want to speak the language mathematicians use.
> We can't make a minilanguage for every domain. If we're making some
> sort of grammar checker, do you expect people to be able to program it
> in English?
No; I'd expect to be able to program it by talking about "indirect
objects" and "antecedants". I'd expect to see talk about unification
parsing, or some other theory of language recognised by experts in the
field.
Ideally, I'd hope that if I showed the last few pages of the code to
an expert in the field, they'd see my mistakes in the basic theory
without being too distracted by the different word order I had to use
in order to easily implement my minilanguage.
This is the goal of writing Forth programs. It's achieved as often as
any other ideal :-).
> > I was explaining what you'd need if you wanted to make this generally
> > useful for what you built it for. Without numeric stability, a
> > mathematical formula syntax is just a way of lying to mathematicians
> > -- you're making it look like your code was trying to compute a
> > formula the mathmaticians recognise, but it really doesn't.
> What is numeric stability?
Hard to explain without a full lecture -- I only have one class in
numeric methods, and that class was specific to linear algebra. But
the basic idea is that computers don't use "real numbers" to do their
math; they use "floating point", and the difference is powerful enough
to cause complex calculations implemented in a naive way to become
extremely inaccurate (or even crash) for some input values.
> > Not true. Although some concatenative languages are parsed, not all
> > need to be. Formally speaking, a concatenative language needs only to
> > be tokenized, and each token mapped onto its semantics. That's it; no
> > parse stage, no parse tree, no lookahead of any kind. Every word is
> > executed (or compiled) immediately as the compiler hits it.
> What's the big difference between tolkenization and parsing? If a
> language has single-pass parsing and tolkenization in one, it's still
> being parsed. Factor is this way.
I think you were thinking of "tolkienization" when you typed that. :-)
Seriously: You can define "parsing" this way if you want, but the
problem is that it becomes synonymous with "tokenization". Parsing in
compiler theory is generally considered to be the job of a push-down
automaton which generates a parse tree, and that isn't needed to
"parse" a concatenative language. So why use a complex word to
describe a simple process, when there's already a word for the simple
process?
(Go ahead, use "parse"; it's easier to type, and perfectly clear. I'm
only making a technical point about how languages are grokked, not
dictating your grammar.)
> > The designers of Lisp thought ahead -- they provided a way to
> > short-circuit the parser or even the reader temporarily. It requires a
> > special construct, though, in order to tell the parser "ignore the
> > following...".
> Factor has a special way of defining words that interact with the
> parser (or tolkenizer, if you want to call it that). I use these words
> in my math thing.
Forth does, too; it calls them "immediate" words (because, of course,
they execute immediately when they're seen by the compiler). But this
doesn't affect the compiler; it doesn't read source any differently
than it did before; it doesn't even know that the immediate word stole
its source and perhaps even replaced it with some different source.
> I only care about concatenative theory inasmuch as it helps you
> actually program. Concatenative theory, or any language theory, works
> best on a minimal, unuseful subset of a language, I think.
That's the traditional thinking, and it's true for the traditional
language theory, applicative theory. It's at least partially untrue
for concatenative theory. We've managed to find a few operations that
are useful but not accounted for by (or allowed within) pure
concatenativity: return stack manipulation and source manipulation.
Both actions are quite esoteric and really very rarely used. I find
that very interesting, and would like to see the theory expanded to
handle those cases, too.
I don't mind if you don't care, though. Go ahead, program, have fun,
and extend the world of useful programs. Just don't complain when I
happen to be interesting in discovering whether or not some obscure
corner of the theory has practical applications. I may be wasting my
time, but perhaps my study will help someone else much later to save
time.
> Daniel Ehrenberg
-Billy
John Cowan — 2005-03-01 20:43:34
William Tanksley, Jr scripsit:
> Hard to explain without a full lecture -- I only have one class in
> numeric methods, and that class was specific to linear algebra. But
> the basic idea is that computers don't use "real numbers" to do their
> math; they use "floating point", and the difference is powerful enough
> to cause complex calculations implemented in a naive way to become
> extremely inaccurate (or even crash) for some input values.
Here's a simple demonstration. This is Perl, but you can rewrite it in
your favorite programming language:
print "yes" if 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1 == 1.0
will not print "yes".
> Forth does, too; it calls them "immediate" words (because, of course,
> they execute immediately when they're seen by the compiler). But this
> doesn't affect the compiler; it doesn't read source any differently
> than it did before; it doesn't even know that the immediate word stole
> its source and perhaps even replaced it with some different source.
All that means is that the definitions of the immediate words are in effect
part of the compiler. Forth's compiler isn't any smaller than any other
system's compiler: it's just more distributed.
--
John Cowan <
cowan@...>
http://www.ccil.org/~cowan
"One time I called in to the central system and started working on a big
thick 'sed' and 'awk' heavy duty data bashing script. One of the geologists
came by, looked over my shoulder and said 'Oh, that happens to me too.
Try hanging up and phoning in again.'" --Beverly Erlebacher
sa@dfa.com — 2005-03-01 20:48:49
in k:
0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1
1.0
/ looks like 1.0, but:
1~0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1
0
/ doesn't exactly match. however:
1=0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1
1
/ because equality uses "fuzz" (comparison tolerance).
but yes, in general, numerical programming requires great
care (to detect and compensate for unstable calculations.)
John Cowan <
cowan@...> wrote on 03/01/2005 03:43:34 PM:
>
> William Tanksley, Jr scripsit:
>
> > Hard to explain without a full lecture -- I only have one class in
> > numeric methods, and that class was specific to linear algebra. But
> > the basic idea is that computers don't use "real numbers" to do their
> > math; they use "floating point", and the difference is powerful enough
> > to cause complex calculations implemented in a naive way to become
> > extremely inaccurate (or even crash) for some input values.
>
> Here's a simple demonstration. This is Perl, but you can rewrite it in
> your favorite programming language:
>
> print "yes" if 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1 == 1.0
>
> will not print "yes".
>
> > Forth does, too; it calls them "immediate" words (because, of course,
> > they execute immediately when they're seen by the compiler). But this
> > doesn't affect the compiler; it doesn't read source any differently
> > than it did before; it doesn't even know that the immediate word stole
> > its source and perhaps even replaced it with some different source.
>
> All that means is that the definitions of the immediate words are in
effect
> part of the compiler. Forth's compiler isn't any smaller than any other
> system's compiler: it's just more distributed.
>
> --
> John Cowan <cowan@...> http://www.ccil.org/~cowan
> "One time I called in to the central system and started working on a big
> thick 'sed' and 'awk' heavy duty data bashing script. One of the
geologists
> came by, looked over my shoulder and said 'Oh, that happens to me too.
> Try hanging up and phoning in again.'" --Beverly Erlebacher
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
sa@dfa.com — 2005-03-01 21:02:17
oops - type less, think more:
1.0~0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1
1
1.0 does indeed match the summation.
but if we set print precision to maximum:
\p 0
then we can see that the sum isn't exactly 1.0:
0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1
0.99999999999999989
whereas:
1.0
1.0
is exactly 1.0
fuzz is good.
sa@... wrote on 03/01/2005 03:48:49 PM:
>
>
>
>
>
>
> in k:
>
> 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1
> 1.0
>
> / looks like 1.0, but:
>
> 1~0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1
> 0
>
> / doesn't exactly match. however:
>
> 1=0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1
> 1
>
> / because equality uses "fuzz" (comparison tolerance).
>
> but yes, in general, numerical programming requires great
> care (to detect and compensate for unstable calculations.)
>
> John Cowan <cowan@...> wrote on 03/01/2005 03:43:34 PM:
>
> >
> > William Tanksley, Jr scripsit:
> >
> > > Hard to explain without a full lecture -- I only have one class in
> > > numeric methods, and that class was specific to linear algebra. But
> > > the basic idea is that computers don't use "real numbers" to do their
> > > math; they use "floating point", and the difference is powerful
enough
> > > to cause complex calculations implemented in a naive way to become
> > > extremely inaccurate (or even crash) for some input values.
> >
> > Here's a simple demonstration. This is Perl, but you can rewrite it in
> > your favorite programming language:
> >
> > print "yes" if 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1 == 1.0
> >
> > will not print "yes".
> >
> > > Forth does, too; it calls them "immediate" words (because, of course,
> > > they execute immediately when they're seen by the compiler). But this
> > > doesn't affect the compiler; it doesn't read source any differently
> > > than it did before; it doesn't even know that the immediate word
stole
> > > its source and perhaps even replaced it with some different source.
> >
> > All that means is that the definitions of the immediate words are in
> effect
> > part of the compiler. Forth's compiler isn't any smaller than any
other
> > system's compiler: it's just more distributed.
> >
> > --
> > John Cowan <cowan@...> http://www.ccil.org/~cowan
> > "One time I called in to the central system and started working on a
big
> > thick 'sed' and 'awk' heavy duty data bashing script. One of the
> geologists
> > came by, looked over my shoulder and said 'Oh, that happens to me too.
> > Try hanging up and phoning in again.'" --Beverly Erlebacher
> >
> >
> >
> > Yahoo! Groups Links
> >
> >
> >
> >
> >
> >
> >
>
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
Slava Pestov — 2005-03-01 21:23:32
William Tanksley, Jr wrote:
> Hard to explain without a full lecture -- I only have one class in
> numeric methods, and that class was specific to linear algebra. But
> the basic idea is that computers don't use "real numbers" to do their
> math; they use "floating point", and the difference is powerful enough
> to cause complex calculations implemented in a naive way to become
> extremely inaccurate (or even crash) for some input values.
Dan's code uses Factor's built-in mathematical operators, which support
arbitrary precision integers, arbitrary precision rationals, complex
numbers, and floating point.
Slava
Slava Pestov — 2005-03-01 21:25:17
John Cowan wrote:
> Here's a simple demonstration. This is Perl, but you can rewrite it in
> your favorite programming language:
>
> print "yes" if 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1 == 1.0
>
> will not print "yes".
1 10 / 1 10 / + 1 10 / + 1 10 / + 1 10 / + 1 10 / + 1 10 / + 1 10 / + 1
10 / + 1 10 / + .
==> 1
This is using exact rational arithmetic. :-)
Slava
William Tanksley, Jr — 2005-03-01 21:30:45
On Tue, 1 Mar 2005 16:02:17 -0500,
sa@... <
sa@...> wrote:
> fuzz is good.
It does help, but of course it isn't related to numeric instability --
unstable algorithms err by enormous amounts. I know you know that
(since you work with a language that's brilliantly designed for such
work), but others here probably don't.
-Billy
William Tanksley, Jr — 2005-03-01 21:41:13
On Tue, 01 Mar 2005 16:23:32 -0500, Slava Pestov <
slava@...> wrote:
> William Tanksley, Jr wrote:
> > Hard to explain without a full lecture -- I only have one class in
> > numeric methods, and that class was specific to linear algebra. But
> > the basic idea is that computers don't use "real numbers" to do their
> > math; they use "floating point", and the difference is powerful enough
> > to cause complex calculations implemented in a naive way to become
> > extremely inaccurate (or even crash) for some input values.
> Dan's code uses Factor's built-in mathematical operators, which support
> arbitrary precision integers, arbitrary precision rationals, complex
> numbers, and floating point.
Not enough -- it all reduces to floating point as soon as you perform
the sqrt. And from then on, you've got to watch out for numeric
instability.
But again, this isn't a bad thing. I'm not slamming that sublanguage;
I like it and think both of you did a great job. I said as much. But
you do have to watch out how you use it. Anything more complex than
the quadratic formula has to be checked carefully, and the order that
the computations are performed in can affect the stability of the
result.
> Slava
-Billy
William Tanksley, Jr — 2005-03-01 22:44:56
John Cowan <
cowan@...> wrote on 03/01/2005 03:43:34 PM:
>William Tanksley, Jr scripsit:
>> Forth does, too; it calls them "immediate" words (because, of course,
>> they execute immediately when they're seen by the compiler). But this
>> doesn't affect the compiler; it doesn't read source any differently
>> than it did before; it doesn't even know that the immediate word stole
>> its source and perhaps even replaced it with some different source.
>All that means is that the definitions of the immediate words are in
>effect part of the compiler.
I don't understand. I'll talk a while to reveal the magnitude of my
incomprehension; please step in and correct me.
By what definition can you claim that? The immediate words have no
access to the internals of the compiler; they're not written by the
people who wrote the compiler; they're stored in source outside of the
compiler's source... They may even be written in a different language.
I can't figure out any way to read that statement that makes any
practical sense.
I can see how in *theory* immediate words could be considered part of
the compiler, but such a theory would quickly become extremely baroque
and useless, since it would have to account for all immediate words
people write.
>Forth's compiler isn't any smaller than any other system's compiler:
For counterexample, consider Ada's compiler. Forth's compiler is
smaller than it. In fact, consider just about any compiler; Forth's
compilers are usually smaller. It's hard to make up for not needing
any parse stage!
>it's just more distributed.
Yes; over all Forth programs ever written. And what use is a statement
like that?
> John Cowan <cowan@...> http://www.ccil.org/~cowan
-Billy
stevan apter — 2005-03-01 22:56:48
----- Original Message -----
From: "William Tanksley, Jr" <wtanksleyjr@...>
To: <concatenative@yahoogroups.com>
Sent: Tuesday, March 01, 2005 4:30 PM
Subject: Re: [stack] The annoying quadratic formul
>
> On Tue, 1 Mar 2005 16:02:17 -0500, sa@... <sa@...> wrote:
> > fuzz is good.
>
> It does help, but of course it isn't related to numeric instability --
> unstable algorithms err by enormous amounts. I know you know that
> (since you work with a language that's brilliantly designed for such
> work), but others here probably don't.
anyone who'll be writing numerical algorithms (a specialized
occupation) will be aware of the problems, and familiar with
a range of techniques for dealing with them. most programmers
never encounter them.
>
> -Billy
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
sa@dfa.com — 2005-03-02 15:01:54
it's worth pointing out (i guess) that while shuffle-notation
can be eliminated by translating abc-def to stack operations
(e.g. swap, pop, dup, &c.), expressions containing general
variables probably cannot - at least, i don't see how. e.g.
in XY notation,
{ [a b] a i b + }
where a is a list of n numbers, leaves
a[1] .. a[n-1] a[n]+b
on the stack. without variables,
[i] dip +
as manfred and brent have pointed out, one distinctive aspect
of the power of joy is that operations can have multiple
results, and this appears to be the heart of the difficulty.
(but you all knew that, right?)
i think the project of eliminating variables by translating
into an enhanced base (i.e. one containing either 'dip' or
operators out of which 'dip' can be defined) is still an
interesting one.
John Cowan — 2005-03-02 19:15:54
William Tanksley, Jr scripsit:
> By what definition can you claim that? The immediate words have no
> access to the internals of the compiler; they're not written by the
> people who wrote the compiler; they're stored in source outside of the
> compiler's source... They may even be written in a different language.
Well, by "the compiler", I mean "all that program, or those programs, whose
purpose is to take a source-code representation of some program and produce
an object-code representation of it", where object code may be p-code,
threaded code, or native code as the case may be. I think this is a suitable
cross-language definition.
Now let us consider the subset of Forth programs which are expressed using
the immedate words in standard Forth (for whichever standard you like)
and no other immediate words. In order to compile such a program, we must
execute not only the core portion of the compiler, the part that arranges
for the word being compiled to be available to future programs and have
the desired effect, but also the definitions of all the immediate words
that are actually being invoked. In essence, the compiler is a table-driven
program which evokes different routines for the input words that it understands,
and a basic routine for the words it does not treat specially.
But this is really no different from compilers that appear monolithic from
the outside.
Now when Forth is extended by adding new immediate words, part of the compiler's
understanding is being farmed out to separately written routines. But any
extensible language such as Common Lisp, Scheme, or even Perl can do this,
and what is happening is that the compiler is being extended by routines
written in the language being compiled, or in C, or even in assembly language.
A syntactically extended language naturally requires an extended compiler.
> I can see how in *theory* immediate words could be considered part of
> the compiler, but such a theory would quickly become extremely baroque
> and useless, since it would have to account for all immediate words
> people write.
Exactly. BTW, is the interface between the core compiler and user-written
immediate word definitions well-defined? In Scheme it is very well defined;
in CL, rather less so; in Perl, well, there is only one implementation.
> For counterexample, consider Ada's compiler. Forth's compiler is
> smaller than it. In fact, consider just about any compiler; Forth's
> compilers are usually smaller. It's hard to make up for not needing
> any parse stage!
Granted, although I think here Scheme and CL give you a reasonable run for
your money.
> Yes; over all Forth programs ever written. And what use is a statement
> like that?
Not all Forth programs: surely not every program introduces new immediate words.
--
John Cowan
jcowan@... www.reutershealth.com www.ccil.org/~cowan
If a soldier is asked why he kills people who have done him no harm, or a
terrorist why he kills innocent people with his bombs, they can always
reply that war has been declared, and there are no innocent people in an
enemy country in wartime. The answer is psychotic, but it is the answer
that humanity has given to every act of aggression in history. --Northrop Frye
Ehrenberg Daniel — 2005-03-03 01:26:47
> >All that means is that the definitions of the immediate words are in
> >effect part of the compiler.
>
> I don't understand. I'll talk a while to reveal the magnitude of my
> incomprehension; please step in and correct me.
>
> By what definition can you claim that? The immediate words have no
> access to the internals of the compiler; they're not written by the
> people who wrote the compiler; they're stored in source outside of the
> compiler's source... They may even be written in a different language.
In Factor, immediate words have access to all of the internal workings
of the compiler and the parser, and they have some limited access to
the internal workings of the interpreter (though only as much as you
can get from reflection). AFAIK, all immediate words that have been
written for CFactor have been written in CFactor. In both Forth and
Factor, the parser is built up largely with immediate words. My new
immediate word |: is as much a part of Factor as : is; they are
defined using the same mechanism.
Daniel Ehrenberg
Slava Pestov — 2005-03-03 02:07:28
Ehrenberg Daniel wrote:
> In Factor, immediate words have access to all of the internal workings
> of the compiler and the parser, and they have some limited access to
> the internal workings of the interpreter (though only as much as you
> can get from reflection).
Actually, Factor's immediate words have access to the parser, and can
affect how a syntax tree generation is created from source. The compiler
on the other hand transforms quotations to machine code and is not
extended with immediate words.
Slava
Rod & Barb Price — 2005-03-03 04:40:44
I've been reading this list for some time, but haven't posted.
I have been wondering about something, however: Can
Joy or Factor or some other Joy-like language be run in an
embedded environment? By "embedded" I mean an
environment where almost all the interpreter lives in ROM
and there is very little RAM. Forth is great for this; I'm just
wondering whether Forth's size advantages carry over to
other concatenative languages.
Also, do Joy, Factor, and relatives use the same range of
implementation methods as Forth? Indirect threading,
subroutine threading, and so forth.
Thanks,
-Rod
phimvt@lurac.latrobe.edu.au — 2005-03-03 08:34:38
On Wed, 2 Mar 2005, Rod & Barb Price wrote:
> I've been reading this list for some time, but haven't posted.
> I have been wondering about something, however: Can
> Joy or Factor or some other Joy-like language be run in an
> embedded environment? By "embedded" I mean an
> environment where almost all the interpreter lives in ROM
> and there is very little RAM. Forth is great for this; I'm just
> wondering whether Forth's size advantages carry over to
> other concatenative languages.
I know nothing about ROM implementations, but I believe it
is a very difficult topic, and I have never ventured there.
My understanding is that implementations of Forth can be
amazingly compact, so -- maybe it fits into ROM. Somebody
else on the group can answer that. But the current implementation
of Joy is probably much larger than that of Forth. On the
Alpha that I use most of the time the executable for the
Joy interpreter is about 175 Kbytes.
> Also, do Joy, Factor, and relatives use the same range of
> implementation methods as Forth? Indirect threading,
> subroutine threading, and so forth.
My version of Joy is written in C, the bulk of it consists
of one C-function for each of the inbuilt primitive Joy operators.
Only a relatively small part deals with scanning, parsing,
handling definitions, and its inbuilt garbage collector.
A much cut down version could be written which has fewer
inbuilts, and relies more on libraries. Whether this could
fit into ROM I cannot tell. As far as I understand the
various forms of threading, my implementation of Joy does
not use anything resembling it.
> Thanks,
>
> -Rod
Welcome to the concatenative mailing group.
- Manfred
William Tanksley, Jr — 2005-03-03 15:49:27
Rod & Barb Price <
rod@...> wrote:
> I've been reading this list for some time, but haven't posted.
> I have been wondering about something, however: Can
> Joy or Factor or some other Joy-like language be run in an
> embedded environment? By "embedded" I mean an
> environment where almost all the interpreter lives in ROM
> and there is very little RAM. Forth is great for this; I'm just
> wondering whether Forth's size advantages carry over to
> other concatenative languages.
Yes, the advantages can carry over; no, not all concatenative
languages choose to live within the restrictions that allow such small
size. I think that Postscript probably comes closest to Forth's size
while providing a huge number of convenient features. But the field is
still young.
vlerq is another new concatenative language that could easily fit into
ROM. (Actually, Thrill is the language's name; vlerq is the name of
the system that includes Thrill and the VM.)
> Also, do Joy, Factor, and relatives use the same range of
> implementation methods as Forth? Indirect threading,
> subroutine threading, and so forth.
Yes, although there have been many more types of Forth threading than
there have been other concatenative languages, so some of the
threading types haven't been tried out in other languages.
> -Rod
-Billy
William Tanksley, Jr — 2005-03-03 20:16:56
John Cowan <
cowan@...> wrote:
> William Tanksley, Jr scripsit:
> > By what definition can you claim that? The immediate words have no
> > access to the internals of the compiler; they're not written by the
> > people who wrote the compiler; they're stored in source outside of the
> > compiler's source... They may even be written in a different language.
> Well, by "the compiler", I mean "all that program, or those programs, whose
> purpose is to take a source-code representation of some program and produce
> an object-code representation of it", where object code may be p-code,
> threaded code, or native code as the case may be. I think this is a suitable
> cross-language definition.
I think I understand your definition. I need to step back for a moment
and gain a larger picture, because I've forgotten why I originally
said what you were correcting above...
Okay, I was specifically claiming that concatenative languages were
special in that it was possible to write "sublanguages" that overrode
the compiler without the compiler even knowing about the override or
the sublanguages.
This claim seems to me to be justified by the fact that forward
parsing is required for applicative languages: an applicative language
must know all of the arguments of the function in order to execute the
function call, but the arguments are placed in the source after the
function's identifier. Concatenative languages in general don't
require forward parsing; the compiler only needs to see the next word
in the source in order to know the next thing to do.
> Now when Forth is extended by adding new immediate words, part of the compiler's
> understanding is being farmed out to separately written routines. But any
> extensible language such as Common Lisp, Scheme, or even Perl can do this,
> and what is happening is that the compiler is being extended by routines
> written in the language being compiled, or in C, or even in assembly language.
> A syntactically extended language naturally requires an extended compiler.
I understand and agree with what you're saying, but it doesn't relate
to my original point. One of the languages you mention, Lisp, attempts
to handle extension in the same way Forth does, but it causes some
problems because Lisp is applicative.
The direct analogue to Forth's immediate words in Lisp is "reader
macros". Normal macros are not equivalent, since they're simply a
special language that's built into the compiler itself to do simple
text manipulations in a way similar to the normal syntax of the
language. Reader macros allow you to define a special character which,
when encountered, will cause the tokenizer to turn over control to
your function, which will consume source code and return a Lisp item
(atom or list) which represents the parse tree for the source code
that was consumed.
Just as in Forth, the compiler never sees the text that's handled by
the reader macro. Just as in Forth, the reader macro decides how much
source to consume, and when control gets handed back to the original
compiler. The only real difference from Forth, in fact, is that in
Lisp reader macros must return the source to a state that the compiler
can handle based on its internal state (such as nesting or position
within special forms). The Forth compiler has no internal state.
There are other problems with Lisp reader macros; but then there are
difficulties with Forth source manipulation that I haven't mentioned.
:-) So I'll stop there.
> > I can see how in *theory* immediate words could be considered part of
> > the compiler, but such a theory would quickly become extremely baroque
> > and useless, since it would have to account for all immediate words
> > people write.
> Exactly. BTW, is the interface between the core compiler and user-written
> immediate word definitions well-defined? In Scheme it is very well defined;
> in CL, rather less so; in Perl, well, there is only one implementation.
How do you mean "well defined"? I have a meaning for that, but I can't
make my meaning not apply to Common Lisp.
There are three ways to produce useful compiled results as an
immediate word. The first is to EVALUATE source; the second is to
POSTPONE words, and the third is to hack values directly into the
dictionary (the compiler's data structure). The first two ways are
well-defined; the third way is entirely undefined. EVALUATE calls the
compiler on the string you give it; POSTPONE compiles a call to a
single word. EVALUATE is dynamic, POSTPONE is static.
> > For counterexample, consider Ada's compiler. Forth's compiler is
> > smaller than it. In fact, consider just about any compiler; Forth's
> > compilers are usually smaller. It's hard to make up for not needing
> > any parse stage!
> Granted, although I think here Scheme and CL give you a reasonable run for
> your money.
There are small Lisps; Lisp is an elegantly simple language. I wasn't
aware that Common Lisp was one of the small Lisps; the implementations
I've seen have been big, as befits anything implementing that
featureful of a standard. Scheme is small, though, and it certainly
doesn't take any real size to implement Common Lisp's reader macros.
> > Yes; over all Forth programs ever written. And what use is a statement
> > like that?
> Not all Forth programs: surely not every program introduces new immediate words.
Touche' to you :-).
Not every one does, but many do.
> John Cowan jcowan@... www.reutershealth.com www.ccil.org/~cowan
-Billy
Slava Pestov — 2005-03-03 21:21:39
Rod & Barb Price wrote:
> I've been reading this list for some time, but haven't posted.
> I have been wondering about something, however: Can
> Joy or Factor or some other Joy-like language be run in an
> embedded environment? By "embedded" I mean an
> environment where almost all the interpreter lives in ROM
> and there is very little RAM.
The Factor runtime is pretty small (60-90kb on x86) and the image can be
stripped down to around 300kb before it stops being recognizable as a
Factor system. Without a listener prompt, a basic image could be built
for just one app in around 100-200kb, but I haven't tried this.
However, there must be enough room in memory for two copies of the heap
(a copying collector is used), and the image must live entirely in RAM.
> Forth is great for this; I'm just
> wondering whether Forth's size advantages carry over to
> other concatenative languages.
Factor uses more memory than Forth for a base system. However any Forth
that attempts to do GUIs, HTTP serving, etc will inevitably have a
vocabulary of OOP and functional techniques. Factor just makes this part
of the kernel.
> Also, do Joy, Factor, and relatives use the same range of
> implementation methods as Forth? Indirect threading,
> subroutine threading, and so forth.
Factor uses a mix of interpretation and compilation. The compiler infers
various static properties and produces faster code. There is a 10x or so
improvement over the interpreter on a mandelbrot fractal benchmark. It
has a pretty inefficient interpreter (it iterates linked lists that
represent quotations). An even more inefficient interpreter can be used
to single-step and trace code. Not all code can be compiled, though;
anything that produces quotations on the fly must run in the
interpreter. About 1900 of 2800 words in the library compile, the rest
run in the interpreter.
Slava
John Cowan — 2005-03-03 22:00:42
Slava Pestov scripsit:
> Factor uses a mix of interpretation and compilation. The compiler infers
> various static properties and produces faster code. There is a 10x or so
> improvement over the interpreter on a mandelbrot fractal benchmark. It
> has a pretty inefficient interpreter (it iterates linked lists that
> represent quotations).
This last is how the Joy[01] interpreter works.
--
Do what you will, John Cowan
this Life's a Fiction
jcowan@...
And is made up of
http://www.reutershealth.com
Contradiction. --William Blake
http://www.ccil.org/~cowan
William Tanksley, Jr — 2005-03-03 23:02:41
sa@... <
sa@...> wrote:
> it's worth pointing out (i guess) that while shuffle-notation
> can be eliminated by translating abc-def to stack operations
> (e.g. swap, pop, dup, &c.), expressions containing general
> variables probably cannot - at least, i don't see how. e.g.
You're right, so long as we don't know the stack effect of the words.
If we know the stack effect, we can perform the translation easily
(that is, the problem is reduced to the shuffle-notation translation).
-Billy
John Cowan — 2005-03-04 14:07:30
William Tanksley, Jr scripsit:
> Okay, I was specifically claiming that concatenative languages were
> special in that it was possible to write "sublanguages" that overrode
> the compiler without the compiler even knowing about the override or
> the sublanguages.
Right.
> The direct analogue to Forth's immediate words in Lisp is "reader
> macros".
In one sense, read-macros are the analogue of immediate words; in another
sense, special forms are, since a CL compiler must understand the syntax
and semantics of the 30- special forms directly.
> Reader macros allow you to define a special character which,
> when encountered, will cause the tokenizer to turn over control to
> your function, which will consume source code and return a Lisp item
> (atom or list) which represents the parse tree for the source code
> that was consumed.
Correct. It's particularly interesting that the standard representation
of lists is handled by a read-macro ('(' is a read-macro character).
> There are three ways to produce useful compiled results as an
> immediate word. The first is to EVALUATE source; the second is to
> POSTPONE words, and the third is to hack values directly into the
> dictionary (the compiler's data structure). The first two ways are
> well-defined; the third way is entirely undefined. EVALUATE calls the
> compiler on the string you give it; POSTPONE compiles a call to a
> single word. EVALUATE is dynamic, POSTPONE is static.
Thanks.
> There are small Lisps; Lisp is an elegantly simple language. I wasn't
> aware that Common Lisp was one of the small Lisps; the implementations
> I've seen have been big, as befits anything implementing that
> featureful of a standard.
Most of the size of CL is in its rich repertoire of ordinary functions,
which don't complicate its compiler. Lisp compilers need have no
knowledge whatsoever of any functions, though they often do know about
some (such as "+"), especially when compiling to p-code or native code.
The compilers in SICP are good examples of how small a Lisp compiler
can be.
> Scheme is small, though, and it certainly doesn't take any real size
> to implement Common Lisp's reader macros.
Oddly, Scheme is in one sense larger than CL. As I said above, there
is a fixed list of special forms that a CL compiler must understand;
everything else is either an ordinary function or a macroexpandable macro.
For example, a CL compiler need not understand "cond", because it's
guaranteed to be able to macroexpand it into something involving the
special form "if". In Scheme, though, there are no such guarantees,
and a Scheme compiler must in principle be able to directly understand
every piece of syntax in the local dialect. (R5RS distinguishes "syntax"
from "library syntax", but this is just conventional.)
Most compilers are system-dependent anyhow, but this does it make it
simpler to write a program-munching program for CL source than for
Scheme source.
--
John Cowan
jcowan@... www.reutershealth.com www.ccil.org/~cowan
"The exception proves the rule." Dimbulbs think: "Your counterexample proves
my theory." Latin students think "'Probat' means 'tests': the exception puts
the rule to the proof." But legal historians know it means "Evidence for an
exception is evidence of the existence of a rule in cases not excepted from."
Ehrenberg Daniel — 2005-03-04 16:08:32
> I think I understand your definition. I need to step back for a moment
> and gain a larger picture, because I've forgotten why I originally
> said what you were correcting above...
>
> Okay, I was specifically claiming that concatenative languages were
> special in that it was possible to write "sublanguages" that overrode
> the compiler without the compiler even knowing about the override or
> the sublanguages.
>
> This claim seems to me to be justified by the fact that forward
> parsing is required for applicative languages: an applicative language
> must know all of the arguments of the function in order to execute the
> function call, but the arguments are placed in the source after the
> function's identifier. Concatenative languages in general don't
> require forward parsing; the compiler only needs to see the next word
> in the source in order to know the next thing to do.
I'm not sure even that's true, eg in the parsing of lists, but in any
case, for most languages (we can leave C++ and Groovy out here),
forward-vs-backwards parsing is the least of our concerns, and we can
focus more on parser extension, which is possible not too difficultly
in both concatenative and applicative languages.
>
>
> I understand and agree with what you're saying, but it doesn't relate
> to my original point. One of the languages you mention, Lisp, attempts
> to handle extension in the same way Forth does, but it causes some
> problems because Lisp is applicative.
>
> The direct analogue to Forth's immediate words in Lisp is "reader
> macros". Normal macros are not equivalent, since they're simply a
> special language that's built into the compiler itself to do simple
> text manipulations in a way similar to the normal syntax of the
> language.
What?!?!?!?! In Lisp, normal macros are used for the vast majority of
uses of immediate words in Forth. They just work differently in the
different languages. Lisp macros are not just for simple text
manipulation, they are for adding new control flow constructs to the
language. They are a very important form of abstraction. Often, it's
easier to make a macro that does something than a reader macro or an
immediate word to do the same because the mechanism is somewhat
higher-level (though the high level can be slightly limiting
sometimes, if you want to do something really radical). Lisp macros
manipulate s-expressions, the universal representation of Lisp code.
It has nothing to do with text. I see nothing wrong with the way Lisp
does macros, except that because it's applicative but not hygenic, you
end up explicitly making tons and tons of gensyms (though that's not
that bad).
> Reader macros allow you to define a special character which,
> when encountered, will cause the tokenizer to turn over control to
> your function, which will consume source code and return a Lisp item
> (atom or list) which represents the parse tree for the source code
> that was consumed.
>
> Just as in Forth, the compiler never sees the text that's handled by
> the reader macro. Just as in Forth, the reader macro decides how much
> source to consume, and when control gets handed back to the original
> compiler. The only real difference from Forth, in fact, is that in
> Lisp reader macros must return the source to a state that the compiler
> can handle based on its internal state (such as nesting or position
> within special forms). The Forth compiler has no internal state.
>
> There are other problems with Lisp reader macros; but then there are
> difficulties with Forth source manipulation that I haven't mentioned.
> :-) So I'll stop there.
The Factor parser also has internal state that must be taken into
account when parsing. Forth has a dictionary, isn't that internal
state? There is no way to avoid internal state, but I don't see it as
a problem.
Daniel Ehrenberg
Ehrenberg Daniel — 2005-03-04 16:15:16
Yes, but if you were insane, you could use an immediate word and go
into the internals of the compiler, couldn't you?
> Actually, Factor's immediate words have access to the parser, and can
> affect how a syntax tree generation is created from source. The compiler
> on the other hand transforms quotations to machine code and is not
> extended with immediate words.
>
> Slava
sa@dfa.com — 2005-03-04 16:41:29
if a fourth instance of this message appears, i *will* go insane.
Ehrenberg Daniel <
microdan@...> wrote on 03/04/2005 11:15:16 AM:
>
> Yes, but if you were insane, you could use an immediate word and go
> into the internals of the compiler, couldn't you?
>
> > Actually, Factor's immediate words have access to the parser, and can
> > affect how a syntax tree generation is created from source. The
compiler
> > on the other hand transforms quotations to machine code and is not
> > extended with immediate words.
> >
> > Slava
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
Slava Pestov — 2005-03-04 16:49:37
Ehrenberg Daniel wrote:
> Yes, but if you were insane, you could use an immediate word and go
> into the internals of the compiler, couldn't you?
Factor's compiler is not connected with the parser, unlike Forth where
both are the same thing. So immediate words hold no relevance to the
compiler -- they only ever matter during parsing.
The compiler goes through two intermediate representations, and it uses
words to represent nodes in these representations, even if the words are
not actual Factor words. If you browse around in the 'compiler'
vocabulary, you will see many symbols with names like #jump, #label, and
so on.
The behaviors of the intermediate representation nodes under various
operations is defined with word properties, which are in some sense a
generalization of immediate words. Each word can be associated with
arbitrary name/value pairs. For example, here is how integer addition is
compiled:
: fixnum-insn ( overflow opcode -- )
#! This needs to be factored.
EAX [ ESI -4 ] MOV
EAX [ ESI ] rot execute
0 JNO fixup
swap compile-call
0 JMP fixup >r
compiled-offset swap patch
ESI 4 SUB
[ ESI ] EAX MOV
r> compiled-offset swap patch ;
\ fixnum+ [
drop \ fixnum+ \ ADD fixnum-insn
] "generator" set-word-property
It is not hard to concieve somebody extending the Factor compiler, as
part of their program, to support some esoteric feature or co-processor
of their CPU. It would require defining some new words and assigning
word properties.
Slava
William Tanksley, Jr — 2005-03-04 18:38:00
On Fri, 04 Mar 2005 11:49:37 -0500, Slava Pestov <
slava@...> wrote:
> Ehrenberg Daniel wrote:
> > Yes, but if you were insane, you could use an immediate word and go
> > into the internals of the compiler, couldn't you?
> Factor's compiler is not connected with the parser, unlike Forth where
> both are the same thing. So immediate words hold no relevance to the
> compiler -- they only ever matter during parsing.
Forth unarguably does not have a parser in this sense. You might say
that it has a tokenizer -- which consists of a function named WORD
with the stack effect ( char -- str count ); but aside from a sequence
of tokens, the compiler doesn't see any structure in Forth code, so
there's nothing for a parser to do, and therefore nothing to interface
with.
The typical code for the compiler's main loop in Forth is:
: NEXTWORD BL WORD FIND ;
: NUMBER, NUMBER POSTPONE LITERAL ;
: COMPILEWORD DUP IF IMMEDIATE? IF EXECUTE ELSE COMPILE, THEN ELSE
NUMBER, THEN ;
: ] BEGIN NEXTWORD COMPILEWORD AGAIN ;
: [ R> R> 2DROP ; IMMEDIATE ( Breaks out of compiler into interpret
mode again )
(Sorry for the all-caps -- this is typical ANSI Forth usage, used to
get around the wording in the standard. I don't approve, but I wasn't
asked. This code was taken from the c2 wiki.)
I fundamentally do not agree with the concept of building a
concatenative language that requires a parser. I fundamentally do
agree with providing a parser and using it for complex forms, such as
literal lists.
By the way, all opinionated ranting about parsers and such aside,
Factor is a really, really cool language. From what I've seen, it's
the most advanced and innovative concatenative language available
(although cK's descendants are very, very cool, and vlerq might
someday grow up to be an awesome environment).
> Slava
-Billy
Slava Pestov — 2005-03-04 18:53:50
William Tanksley, Jr wrote:
> The typical code for the compiler's main loop in Forth is:
>
> : NEXTWORD BL WORD FIND ;
> : NUMBER, NUMBER POSTPONE LITERAL ;
> : COMPILEWORD DUP IF IMMEDIATE? IF EXECUTE ELSE COMPILE, THEN ELSE
> NUMBER, THEN ;
> : ] BEGIN NEXTWORD COMPILEWORD AGAIN ;
> : [ R> R> 2DROP ; IMMEDIATE ( Breaks out of compiler into interpret
> mode again )
This is *exactly* how the Factor parser works. It reads tokens one at a
time, and looks them up in the dictionary. If the token corresponds to a
parsing word, it is executed immediately. The only difference is that
instead of calling COMPILE, or POSTPONE LITERAL it just appends words
and numbers as they are read to a list. So the output is not compiled
tokens in the dictionary, but a list (with possibly nested sublists).
> I fundamentally do not agree with the concept of building a
> concatenative language that requires a parser. I fundamentally do
> agree with providing a parser and using it for complex forms, such as
> literal lists.
In Factor, lists are read using the [ and ] immediate words.
: [ f ; parsing
: ] reverse swons ; parsing
Similarly, all non-concatenative aspects of syntax, such as the : and ;
words that delimit word definitions, are immediate words.
So the difference between Factor and Forth is that in Forth, the parser
generates executable code directly. In Factor, code generation is
decoupled from parsing, which gives us a superior multi-pass compiler.
Slava
William Tanksley, Jr — 2005-03-04 20:45:59
John Cowan <
cowan@...> wrote:
> William Tanksley, Jr scripsit:
> > Okay, I was specifically claiming that concatenative languages were
> > special in that it was possible to write "sublanguages" that overrode
> > the compiler without the compiler even knowing about the override or
> > the sublanguages.
> > The direct analogue to Forth's immediate words in Lisp is "reader
> > macros".
> In one sense, read-macros are the analogue of immediate words; in another
> sense, special forms are, since a CL compiler must understand the syntax
> and semantics of the 30- special forms directly.
I don't understand how that makes the two analogous. It seems to me
like it would make them opposites -- I chose reader macros precisely
because the compiler doesn't have to understand their syntax and
semantics, just as a Forth compiler doesn't understand the syntax and
semantics of immediate words.
I'm seriously confused by your claim of analogy on precisely the point
at which the two things are entirely different.
> > There are small Lisps; Lisp is an elegantly simple language. I wasn't
> > aware that Common Lisp was one of the small Lisps; the implementations
> > I've seen have been big, as befits anything implementing that
> > featureful of a standard.
> Most of the size of CL is in its rich repertoire of ordinary functions,
> which don't complicate its compiler. Lisp compilers need have no
> knowledge whatsoever of any functions, though they often do know about
> some (such as "+"), especially when compiling to p-code or native code.
> The compilers in SICP are good examples of how small a Lisp compiler
> can be.
Good book, fun. I understand it's online now, so worth reading if
someone here hasn't. Although it has no discussion of concatenative
language, it thouroughly explores the nature of applicative languages,
and gets into the problems with them very early.
> > Scheme is small, though, and it certainly doesn't take any real size
> > to implement Common Lisp's reader macros.
> Oddly, Scheme is in one sense larger than CL. As I said above, there
> is a fixed list of special forms that a CL compiler must understand;
> everything else is either an ordinary function or a macroexpandable macro.
> For example, a CL compiler need not understand "cond", because it's
> guaranteed to be able to macroexpand it into something involving the
> special form "if". In Scheme, though, there are no such guarantees,
> and a Scheme compiler must in principle be able to directly understand
> every piece of syntax in the local dialect. (R5RS distinguishes "syntax"
> from "library syntax", but this is just conventional.)
It is true that Scheme's macros are very complex; Scheme in general
has much more reliance on verbose syntax than does Lisp.
> John Cowan jcowan@... www.reutershealth.com www.ccil.org/~cowan
-Billy
William Tanksley, Jr — 2005-03-04 21:14:48
Ehrenberg Daniel <
microdan@...> wrote:
> > Okay, I was specifically claiming that concatenative languages were
> > special in that it was possible to write "sublanguages" that overrode
> > the compiler without the compiler even knowing about the override or
> > the sublanguages.
> > This claim seems to me to be justified by the fact that forward
> > parsing is required for applicative languages: an applicative language
> > must know all of the arguments of the function in order to execute the
> > function call, but the arguments are placed in the source after the
> > function's identifier. Concatenative languages in general don't
> > require forward parsing; the compiler only needs to see the next word
> > in the source in order to know the next thing to do.
> I'm not sure even that's true, eg in the parsing of lists,
Lists are not an element of program syntax, but rather are a type of
literal. Admittedly they can become a complex literal, and as such
they definitely require parsing. This is the major reason why Forth
does not have them. (I'm not arguing against them, by the way; I think
they're very good things in general, but not for the purposes of
Forth.)
> but in any
> case, for most languages (we can leave C++ and Groovy out here),
> forward-vs-backwards parsing is the least of our concerns, and we can
> focus more on parser extension, which is possible not too difficultly
> in both concatenative and applicative languages.
Boy: Do not try and extend the parser. That's impossible. Instead only
try to realize the truth.
Neo: What truth?
Boy: There is no parser.
Neo: There is no parser?
Boy: Then you'll see that it is not the parser that is extended, it is
only the program itself.
Parsing is not "the least of our concerns." Talking about "forward
versus backward" parsing (what does "backward parsing" mean, anyhow?)
obscures the issue. One type of language requires no parsing of its
own, and thereby frees the programmer to do any parsing needed without
interference with the original compiler. The other type of language
must do parsing, and thereby requires the programmer to constrain his
source-parsing within acceptable limits.
> > I understand and agree with what you're saying, but it doesn't relate
> > to my original point. One of the languages you mention, Lisp, attempts
> > to handle extension in the same way Forth does, but it causes some
> > problems because Lisp is applicative.
> > The direct analogue to Forth's immediate words in Lisp is "reader
> > macros". Normal macros are not equivalent, since they're simply a
> > special language that's built into the compiler itself to do simple
> > text manipulations in a way similar to the normal syntax of the
> > language.
> What?!?!?!?! In Lisp, normal macros are used for the vast majority of
> uses of immediate words in Forth.
Yes, normal macros are functionally used similarly to how Forth
immediate words are used; however, they work entirely differently.
Since I'm comparing the compilers, it's most appropriate to consider
how they work, not how they are used.
> They just work differently in the different languages.
Exactly.
> Often, it's
> easier to make a macro that does something than a reader macro or an
> immediate word to do the same because the mechanism is somewhat
> higher-level
Absolutely. That's its purpose, you might say.
Interestingly, Forth's immediate words have both the power of Lisp's
reader macros and the high-level nature of Lisp's macros.
> sometimes, if you want to do something really radical). Lisp macros
> manipulate s-expressions, the universal representation of Lisp code.
> It has nothing to do with text.
You're right here; I was wrong.
> I see nothing wrong with the way Lisp
> does macros, except that because it's applicative but not hygenic, you
> end up explicitly making tons and tons of gensyms (though that's not
> that bad).
The old Scheme versus Lisp debate :-). Personally, I'm pretty much on
the Lisp side here, although I sympathise with the basic idea of
Schemers.
Note that this debate never arises with Forth -- there's nothing even
like it, because there are no variable namespaces. Macros, once again,
fit seamlessly into a concatenative language, with no hidden pitfalls.
(It's not quite true that there are no hidden pitfalls. Because there
are two different ways to form a macro, one static and one dynamic,
you must be sure that you've chosen the right one. Usually in Forth
static is the desired meaning. This problem would also be present in
Lisp, but the definition of Lisp requires that all bindings be
dynamic, so that programmers cannot choose which type of binding they
want -- a procrustean way to remove the problem.)
> > Reader macros allow you to define a special character which,
> > when encountered, will cause the tokenizer to turn over control to
> > your function, which will consume source code and return a Lisp item
> > (atom or list) which represents the parse tree for the source code
> > that was consumed.
> > Just as in Forth, the compiler never sees the text that's handled by
> > the reader macro. Just as in Forth, the reader macro decides how much
> > source to consume, and when control gets handed back to the original
> > compiler. The only real difference from Forth, in fact, is that in
> > Lisp reader macros must return the source to a state that the compiler
> > can handle based on its internal state (such as nesting or position
> > within special forms). The Forth compiler has no internal state.
> > There are other problems with Lisp reader macros; but then there are
> > difficulties with Forth source manipulation that I haven't mentioned.
> > :-) So I'll stop there.
> The Factor parser also has internal state that must be taken into
> account when parsing. Forth has a dictionary, isn't that internal
> state? There is no way to avoid internal state, but I don't see it as
> a problem.
Factor does have a parser, so macros for Factor have to take its state
into account. I don't know what happens when and if Factor finds an
immediate word in the middle of a nested quotation. Forth does not
have a parser, so the problem does not arise. Internal state /per se/
is not a problem; only internal state that affects the operation of
the macro is a problem.
The Forth dictionary is internal state, but it doesn't have anything
to do with macro use; a Forth macro should read source, compute the
replacement source, and evaluate the replacement to cause the compiler
to see the new source as though it were inserted into the input
stream. Nothing about the dictionary there, except the same thing
you'd see in normal operation.
> Daniel Ehrenberg
-Billy
Slava Pestov — 2005-03-04 23:05:47
William Tanksley, Jr wrote:
> The old Scheme versus Lisp debate :-). Personally, I'm pretty much on
> the Lisp side here, although I sympathise with the basic idea of
> Schemers.
>
> Note that this debate never arises with Forth -- there's nothing even
> like it, because there are no variable namespaces.
This certainly does arise in Forth! And many Forths let one define
multiple named vocabularies of words.
Consider the classic Forth variables:
: VARIABLE CREATE , DOES> ;
VARIABLE x 5 x ! ... x @
Here, you cannot have another word named 'x'...
> Factor does have a parser, so macros for Factor have to take its state
> into account. I don't know what happens when and if Factor finds an
> immediate word in the middle of a nested quotation.
In Factor, [ and ] are immedaite words, and the fact that you can write,
for example, [ 1 2 [ 3 4 ] 5 6 ] demonstrates they work ;-)
The Factor parser maintains nested state when reading quotations,
vectors, hashtables and other objects in the same way the Forth parser
maintains state when reading nested control structure; using the stack.
Forth and Factor both have parsers -- source code is converted into some
kind of structure in an inductive fashion.
Slava
William Tanksley, Jr — 2005-03-05 02:46:44
Slava Pestov <
slava@...> wrote:
> This is *exactly* how the Factor parser works. It reads tokens one at a
> time, and looks them up in the dictionary. If the token corresponds to a
> parsing word, it is executed immediately. The only difference is that
> instead of calling COMPILE, or POSTPONE LITERAL it just appends words
> and numbers as they are read to a list. So the output is not compiled
> tokens in the dictionary, but a list (with possibly nested sublists).
In other words, Factor (1) has a defined dictionary format and (2)
uses lists to store the contents of the dictionary. Forth doesn't have
a defined format, and uses arrays. (You could, I assume, have used the
names COMPILE, and POSTPONE LITERAL.)
> > I fundamentally do not agree with the concept of building a
> > concatenative language that requires a parser. I fundamentally do
> > agree with providing a parser and using it for complex forms, such as
> > literal lists.
> In Factor, lists are read using the [ and ] immediate words.
Yes, I've seen a similar thing done for Forth, although of course the
result isn't as pleasant as Factor. As much as I like Forth, there are
many excellent things it doesn't do.
> Similarly, all non-concatenative aspects of syntax, such as the : and ;
> words that delimit word definitions, are immediate words.
Yup, makes sense.
> So the difference between Factor and Forth is that in Forth, the parser
> generates executable code directly. In Factor, code generation is
> decoupled from parsing, which gives us a superior multi-pass compiler.
Actually, the only difference I see is that Factor defines the parse
tree and gives the program access to it. Forth doesn't. This gives a
Factor program the ability to do things that a Forth program can't,
but doesn't give any optimization advantages. There's nothing
preventing Forth from being parsed into an intermediate format -- in
fact, unlike Factor it could be parsed into some other format (perhaps
a dataflow format rather than a stack VM). As I'm sure you know, there
are good Forth optimisers.
But I don't want to try to give a commercial for Forth. That would be
a lie; Factor is almost certainly easier to optimise than Forth, and
for good reason; it's designed to be. There are many decisions that
make it so.
I do agree at last that Factor clearly has a parser, and yet has the
same basic syntax as Forth. Thus, you could consider Forth as having a
parser if you wanted to; but the idea lacks definition.
> Slava
-Billy
William Tanksley, Jr — 2005-03-05 05:17:37
Slava Pestov <
slava@...> wrote:
> William Tanksley, Jr wrote:
> > The old Scheme versus Lisp debate :-). Personally, I'm pretty much on
> > the Lisp side here, although I sympathise with the basic idea of
> > Schemers.
> > Note that this debate never arises with Forth -- there's nothing even
> > like it, because there are no variable namespaces.
> This certainly does arise in Forth! And many Forths let one define
> multiple named vocabularies of words.
Could you provide an example? I'm tempted to argue, but I respect your
knowledge.
> Consider the classic Forth variables:
> : VARIABLE CREATE , DOES> ;
> VARIABLE x 5 x ! ... x @
> Here, you cannot have another word named 'x'...
I don't get your point at all. It doesn't seem to have anything to do
with any of the issues we're discussing. It's not even true -- of
course we can have another word named 'x'. Of course, that'll change
the meaning of textual occurances of 'x', but that's hardly
surprising; that's what a "redefinition" does.
And it doesn't seem to have anything to do with macros -- unless, of
course, someone confused a late-binding macro (built using strings and
EVALUATE) with an early-binding macro (built using POSTPONE and
literals). For example, suppose someone defined : get-x s" x @"
evaluate ; immediate. The resulting word will change its behavior if @
or x is redefined. But if this is a problem, then the entire language
of Lisp is a problem -- if you redefine a word in Lisp, all functions
previously defined are supposed to change behavior to match the newest
definition. So Lisp acts this way even without macros (and it's a
useful feature, not a bug).
The actual problem I'm discussing appears in macros and reader macros
in which local variables and parameter names can leak across lexically
scoped boundaries, doing odd things (sometimes useful things) with the
parameters of the macros.
> > Factor does have a parser, so macros for Factor have to take its state
> > into account. I don't know what happens when and if Factor finds an
> > immediate word in the middle of a nested quotation.
> In Factor, [ and ] are immedaite words, and the fact that you can write,
> for example, [ 1 2 [ 3 4 ] 5 6 ] demonstrates they work ;-)
Good point.
> The Factor parser maintains nested state when reading quotations,
> vectors, hashtables and other objects in the same way the Forth parser
> maintains state when reading nested control structure; using the stack.
Again, a stunning blow. For those looking on, Forth does use parsing
structures like : ; BEGIN AGAIN DO LOOP IF THEN. I can try to wiggle
out of this argument by claiming that those words don't do complex
source lookahead, so the jobs of parsing and lookahead are entirely
distinct; in fact they are, but all this does for me is show that
Forth's built-in parser is something like an LL parser rather than a
more sophisticated LR. This is NOT what I wanted to prove :-).
> Forth and Factor both have parsers -- source code is converted into some
> kind of structure in an inductive fashion.
You've very nicely shown this; also, I think it could be shown that
some amount of parsing in this sense is /always/ needed. It must be
possible at least to form a forward and backward jump, and both
require a limited "parsing" in the sense of data passed along from one
immediate word to another.
Joy takes a different approach; instead of providing forward and
backward jump words, it uses quotations, which allow it to abandon
_all_ other parsing, and even immediate words (this abandonment allows
it to focus on its primary purpose, explaining concatenative rules,
without having to deal with the complexities that compile-time
execution brings).
But here's the problem. Any language in which words get "tied
together", either by parsing words (Forth and Factor) or by explicit
syntax (Joy) isn't fully concatenative wherever the parsing words
affect the outcome. Question: is it possible to express concatenative
theory in a way that accommodates that? We already know that treating
quotations and lists interchangably makes it very hard to reason about
the contents of quotations (I understand that Factor currently suffers
from that, IIRC quotations can't be optimized).
Is there possibly some construct that neatly avoids all of this?
Continuations, perhaps?
> Slava
-Billy
Slava Pestov — 2005-03-05 05:48:18
William Tanksley, Jr wrote:
> Could you provide an example? I'm tempted to argue, but I respect your
> knowledge.
gforth provides "wordlists", for example.
> You've very nicely shown this; also, I think it could be shown that
> some amount of parsing in this sense is /always/ needed. It must be
> possible at least to form a forward and backward jump, and both
> require a limited "parsing" in the sense of data passed along from one
> immediate word to another.
Well, theoretically one can have a Factor or Lisp system without a
parser -- all programs are input by editing cons cells directly in some
kind of struture editor. Recognizing user input could be taken as
parsing, though.
> But here's the problem. Any language in which words get "tied
> together", either by parsing words (Forth and Factor) or by explicit
> syntax (Joy) isn't fully concatenative wherever the parsing words
> affect the outcome. Question: is it possible to express concatenative
> theory in a way that accommodates that?
I believe so; just reason about your quotation after the parsing has
already been done. For example, [ 1 2 [ 3 4 ] 5 ] is a list of four
elements, and the third one is another list of two elements, [ 3 4 ].
You don't have to worry about how this got constructed or how the [ ]
parsing words were called.
> We already know that treating
> quotations and lists interchangably makes it very hard to reason about
> the contents of quotations (I understand that Factor currently suffers
> from that, IIRC quotations can't be optimized).
Quotations can be optimized. For example, if you compile a word
definition containing [ * ] map, it generates efficient code by inlining
the definition of map and "lifting" the quotation to its call site. In
compiled code, no actual quotations are ever passed around on the stack.
> Is there possibly some construct that neatly avoids all of this?
> Continuations, perhaps?
I'm not sure what continuations have to do with this. Factor has
continuations, but they are very hard to reason about in a static
manner. For example, words using continuations do not compile at this stage.
Slava
Ehrenberg Daniel — 2005-03-05 12:40:06
> Joy takes a different approach; instead of providing forward and
> backward jump words, it uses quotations, which allow it to abandon
> _all_ other parsing, and even immediate words (this abandonment allows
> it to focus on its primary purpose, explaining concatenative rules,
> without having to deal with the complexities that compile-time
> execution brings).
How could joy not have a parser for things like DEFINE x == y.? And
how can quotations be parsed without a parser?
If a language is supposed to explain something, it should have the
features that usable concatenative languages have, like immediate
words and mutable datastructures. Otherwise, the whole thing is just
pointless.
Daniel Ehrenberg
stevan apter — 2005-03-05 14:21:51
----- Original Message -----
From: "Ehrenberg Daniel" <microdan@...>
To: <concatenative@yahoogroups.com>
Sent: Saturday, March 05, 2005 7:40 AM
Subject: Re: [stack] Extensible compilers
>
> > Joy takes a different approach; instead of providing forward and
> > backward jump words, it uses quotations, which allow it to abandon
> > _all_ other parsing, and even immediate words (this abandonment allows
> > it to focus on its primary purpose, explaining concatenative rules,
> > without having to deal with the complexities that compile-time
> > execution brings).
>
> How could joy not have a parser for things like DEFINE x == y.? And
> how can quotations be parsed without a parser?
consider a simple language whose elements are integers and lists.
given a piece of source-text:
1 2 [ 30 40 [ 500 600 700 ] ] 8 9
tokenize (break the text into unevaluated parts):
1
2
[
30
40
[
500
600
700
]
]
8
9
the evaluator e has four words:
Z terminate - we're done evaluating
L left bracket - doesn't look ahead
R right bracket
N number
at any point, e has three pieces of information:
x the stack
y a pointer to the end of the stack
z the remaining list of tokens to process
in my implementation (below) the pointer is a vector, and pushing an
item onto the stack consists of appending that item to the stack at
depth.
if there are no tokens left, e calls Z and exits with the result stack.
if the next token is [, it calls L. L pushes an empty list onto the
stack and "increments" the stack pointer.
if the next token is ], it calls R. R "decrements" the stack pointer.
if the next token is a number, it calls N. N pushes the number onto
the stack.
if you think there is parsing here, i'd like to know where it is.
the string is tokenized, and each token is processed by a word
corresponding to the type of the token.
in k:
/ string:
s:"1 2 [ 30 40 [ 500 600 700 ] ] 8 9"
/ tokenize:
t:(((&s=" ")_ s:" ",s)_dv'" ")_dv""
/ evaluate:
e:{
:[z~() ;Z
"["=**z ;L
"]"=**z ;R
N][x;y;z]}
/ words:
Z:{(x;y;z)} / terminate
L:{(.[x;y;,;,()];y,#x . y;1_ z)} / left bracket
R:{(x;-1_ y;1_ z)} / right bracket
N:{(.[x;y;,;.*z];y;1_ z)} / number
/ run:
*(e .)/(();!0;t)
/ result:
(1
2
(30
40
500 600 700)
8
9)
>
> If a language is supposed to explain something, it should have the
> features that usable concatenative languages have, like immediate
> words and mutable datastructures. Otherwise, the whole thing is just
> pointless.
that seems unduly harsh.
a language can be used to shed light on concepts (and therefore serve
to explain something interesting) and otherwise be quite useless (e.g.
unlambda.)
the goals and constraints of real-world application-programming, not
to mention the physical limitations of actual computers, force us
away from such languages, but that hardly makes them "pointless."
>
> Daniel Ehrenberg
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
John Cowan — 2005-03-05 15:10:45
stevan apter scripsit:
> consider a simple language whose elements are integers and lists.
Okay.
[pseudo-code for parsing the language snipped]
> if you think there is parsing here, i'd like to know where it is.
You have just described the parser. A parser is a component that takes
a stream of tokens and returns a data structure of some sort. This data
structure can be traversed by an interpreter, converted back into text
by a pretty-printer, further transformed by a compiler, or what have you.
Parsing and interpreting can be intertwingled, creating a recursive-descent
interpreter. However, except for very simple languages like dc and bc
(calculators, basically), this is not normally done; it is usually worthwhile
having a separate parser and an interpreter that can execute the parser's
output more than once.
> the string is tokenized, and each token is processed by a word
> corresponding to the type of the token.
Yes. That is what parsers do.
--
You let them out again, Old Man Willow! John Cowan
What you be a-thinking of? You should not be waking!
jcowan@...
Eat earth! Dig deep! Drink water! Go to sleep! www.reutershealth.com
Bombadil is talking. www.ccil.org/~cowan
Andreas Bolka — 2005-03-05 15:50:23
Saturday, March 5, 2005, 4:10:45 PM, John wrote:
> stevan apter scripsit:
>> if you think there is parsing here, i'd like to know where it is.
> You have just described the parser. A parser is a component that
> takes a stream of tokens and returns a data structure of some sort.
> This data structure can be traversed by an interpreter, converted
> back into text by a pretty-printer, further transformed by a
> compiler, or what have you.
Some people call a lexer/scanner/tokenizer a lexical parser, others
consider a thing to only be a parser when abstract syntax trees/parse
trees and formal grammars are involved (people who call scanners
"lexical parsers" often call things involving formal grammars
"syntactical" or "grammatical" parsers).
From my perspective it looks as if members of both camps are currently
clashing on this list.
--
Best regards,
Andreas mailto:
andreas.bolka@...
stevan apter — 2005-03-05 16:25:24
From: "John Cowan" <
cowan@...>
>
> > the string is tokenized, and each token is processed by a word
> > corresponding to the type of the token.
>
> Yes. That is what parsers do.
yes, i think you're right about this.
and yet, i am still inclined to use billy's terminology,
according to which joy (and concatenative languages generally)
"have no syntax."
William Tanksley, Jr — 2005-03-05 16:24:19
Ehrenberg Daniel <
microdan@...> wrote:
> > Joy takes a different approach; instead of providing forward and
> > backward jump words, it uses quotations, which allow it to abandon
> > _all_ other parsing, and even immediate words (this abandonment allows
> > it to focus on its primary purpose, explaining concatenative rules,
> > without having to deal with the complexities that compile-time
> > execution brings).
> How could joy not have a parser for things like DEFINE x == y.?
That's deliberately constructed to NOT appear like part of the
language, for other reasons -- specifically, to keep from exposing the
fact that definitions alter the dictionary. It would be easily
possible to make a definition word that doesn't require any parsing,
but it would make dictionary modifications a part of the language.
> And how can quotations be parsed without a parser?
I said "any other parsing". Such as, for example, the parsing usually
required for IF. My goal was to communicate that Joy chose to use a
fairly complex parser for a single general-purpose feature
(quotations) in order to remove the need for parsers anywhere else.
> If a language is supposed to explain something, it should have the
> features that usable concatenative languages have, like immediate
> words and mutable datastructures. Otherwise, the whole thing is just
> pointless.
This is wrong on two levels. First, your examples are bad: immediate
words aren't in most concatenative languages, and as much as I like
them, they aren't themselves concatenative; and mutable datastructures
are entirely irrelevant to usefulness. Second, though, even a simple
language that wound up too hard to actually use could serve as a
useful proof or disproof of concept.
> Daniel Ehrenberg
-Billy
stevan apter — 2005-03-05 16:50:40
----- Original Message -----
From: "William Tanksley, Jr" <wtanksleyjr@...>
To: <concatenative@yahoogroups.com>
Sent: Saturday, March 05, 2005 11:24 AM
Subject: Re: [stack] Extensible compilers
>
> Ehrenberg Daniel <microdan@...> wrote:
> > > Joy takes a different approach; instead of providing forward and
> > > backward jump words, it uses quotations, which allow it to abandon
> > > _all_ other parsing, and even immediate words (this abandonment allows
> > > it to focus on its primary purpose, explaining concatenative rules,
> > > without having to deal with the complexities that compile-time
> > > execution brings).
>
> > How could joy not have a parser for things like DEFINE x == y.?
>
> That's deliberately constructed to NOT appear like part of the
> language, for other reasons -- specifically, to keep from exposing the
> fact that definitions alter the dictionary. It would be easily
> possible to make a definition word that doesn't require any parsing,
> but it would make dictionary modifications a part of the language.
>
> > And how can quotations be parsed without a parser?
>
> I said "any other parsing". Such as, for example, the parsing usually
> required for IF. My goal was to communicate that Joy chose to use a
> fairly complex parser for a single general-purpose feature
> (quotations) in order to remove the need for parsers anywhere else.
the nested-bracket notation for lists can be eliminated in favor
of a purely functional form:
, expects a non-negative number k on top of the stack, and
below that, at least k items. it replaces k, and the k items
below that, with a list of the k items. ([] is 0 ,)
here's an implementation:
/ string:
s:"1 2 30 40 500 600 700 3 , 3 , 8 9"
/ tokenize:
t:(((&s=" ")_ s:" ",s)_dv'" ")_dv""
/ evaluate:
e:{
:[y~() ;Z
","=**y ;E
N][x;y]}
/ words:
Z:{(x;y)} / terminate
E:{(((- 1+*|x)_ x),,(-*|x)#-1_ x;1_ y)} / enlist
N:{(x,.*y;1_ y)} / number
/ run:
*(e .)/(();t)
/ result (as before):
(1
2
(30
40
500 600 700)
8
9)
you'll notice that i said "lists" and not "quotations". as far as i
can see, in order to do quotations functionally, you need a special
syntax for quoted *atoms*. alternatively, you can have an atom-quotation
word which looks at most one token forward on the queue:
Q:{(x,.*1_ y;2_ y)}
e.g.
2 3 ` + 3 ,
[2 3 +]
>
> > If a language is supposed to explain something, it should have the
> > features that usable concatenative languages have, like immediate
> > words and mutable datastructures. Otherwise, the whole thing is just
> > pointless.
>
> This is wrong on two levels. First, your examples are bad: immediate
> words aren't in most concatenative languages, and as much as I like
> them, they aren't themselves concatenative; and mutable datastructures
> are entirely irrelevant to usefulness. Second, though, even a simple
> language that wound up too hard to actually use could serve as a
> useful proof or disproof of concept.
>
> > Daniel Ehrenberg
>
> -Billy
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
William Tanksley, Jr — 2005-03-05 17:04:16
stevan apter <
sa@...> wrote:
> consider a simple language whose elements are integers and lists.
> given a piece of source-text:
> 1 2 [ 30 40 [ 500 600 700 ] ] 8 9
That language definitely requires parsing -- remember your
computational theory courses? Balancing parentheses (or brackets)
requires a push-down automaton, which is the usual way to build a
parser.
It's pretty obvious that a concatenative language without list
literals or control structures would be non-parsing. It would also be
pretty hard to use, or at least I can't figure out a way to make it
easy. I remember when we were talking about simple bases, we came up
with the way to build a completely syntax-free language, but although
the result was complete, it wasn't at all easy to use.
It's not really important to me whether a usable language could be
constucted using only modern concatenative theory. I'd personally
rather expand the theory until it explains useful languages :-). But
it would nonetheless be interesting...
> if you think there is parsing here, i'd like to know where it is.
> the string is tokenized, and each token is processed by a word
> corresponding to the type of the token.
Well, for one, that's a pretty typical way to build a parser :-).
That's how yacc works. Well, yacc is more complex, but that's the
basic description.
-Billy
William Tanksley, Jr — 2005-03-05 17:06:32
stevan apter <
sa@...> wrote:
> and yet, i am still inclined to use billy's terminology,
> according to which joy (and concatenative languages generally)
> "have no syntax."
Thank you :-). I do think that there's some truth in that -- at least
there are large stretches of the language that can be examined without
considering syntax.
-Billy
Shae Matijs Erisson — 2005-03-05 17:22:16
"William Tanksley, Jr" <
wtanksleyjr@...> writes:
> It's pretty obvious that a concatenative language without list
> literals or control structures would be non-parsing. It would also be
> pretty hard to use, or at least I can't figure out a way to make it
> easy. I remember when we were talking about simple bases, we came up
> with the way to build a completely syntax-free language, but although
> the result was complete, it wasn't at all easy to use.
Can you give more info on this? Sounds quite interesting.
--
Shae Matijs Erisson -
http://www.ScannedInAvian.com/ - Sockmonster once said:
You could switch out the unicycles for badgers, and the game would be the same.
stevan apter — 2005-03-05 18:04:37
----- Original Message -----
From: "William Tanksley, Jr" <wtanksleyjr@...>
To: <concatenative@yahoogroups.com>
Sent: Saturday, March 05, 2005 12:04 PM
Subject: Re: [stack] Extensible compilers
>
> stevan apter <sa@...> wrote:
> > consider a simple language whose elements are integers and lists.
> > given a piece of source-text:
> > 1 2 [ 30 40 [ 500 600 700 ] ] 8 9
>
> That language definitely requires parsing -- remember your
> computational theory courses?
actually? no.
Balancing parentheses (or brackets)
> requires a push-down automaton, which is the usual way to build a
> parser.
>
> It's pretty obvious that a concatenative language without list
> literals or control structures would be non-parsing. It would also be
> pretty hard to use, or at least I can't figure out a way to make it
> easy. I remember when we were talking about simple bases, we came up
> with the way to build a completely syntax-free language, but although
> the result was complete, it wasn't at all easy to use.
we're used to
[10 20 30]
but why not simply consider that mere syntactic sugar for the underlying
functional form
10 20 30 3 ,
>
> It's not really important to me whether a usable language could be
> constucted using only modern concatenative theory. I'd personally
> rather expand the theory until it explains useful languages :-). But
> it would nonetheless be interesting...
>
> > if you think there is parsing here, i'd like to know where it is.
> > the string is tokenized, and each token is processed by a word
> > corresponding to the type of the token.
>
> Well, for one, that's a pretty typical way to build a parser :-).
> That's how yacc works. Well, yacc is more complex, but that's the
> basic description.
i'll take your word for it.
>
> -Billy
>
>
>
> Yahoo! Groups Links
>
>
>
>
>
>
>
William Tanksley, Jr — 2005-03-05 18:03:24
Shae Matijs Erisson <
shae@...> wrote:
Hey! I like that domain name.
> "William Tanksley, Jr" <wtanksleyjr@...> writes:
> > It's pretty obvious that a concatenative language without list
> > literals or control structures would be non-parsing. It would also be
> > pretty hard to use, or at least I can't figure out a way to make it
> > easy. I remember when we were talking about simple bases, we came up
> > with the way to build a completely syntax-free language, but although
> > the result was complete, it wasn't at all easy to use.
> Can you give more info on this? Sounds quite interesting.
Brent Kerby wrote a quite exhaustive paper on it at
http://tunes.org/~iepos/joy.html. Check it out, and we can talk about
it...
> Shae Matijs Erisson - http://www.ScannedInAvian.com/ - Sockmonster once said:
-Billy
William Tanksley, Jr — 2005-03-05 18:10:03
stevan apter <
sa@...> wrote:
> From: "William Tanksley, Jr" <wtanksleyjr@...>
> > stevan apter <sa@...> wrote:
> > > consider a simple language whose elements are integers and lists.
> > > given a piece of source-text:
> > > 1 2 [ 30 40 [ 500 600 700 ] ] 8 9
> > That language definitely requires parsing -- remember your
> > computational theory courses?
> actually? no.
Been a while :-). Well, in short and specific, any language that
requires two separate entities -- like opening and closing brackets --
to be tied together requires parsing.
> > Balancing parentheses (or brackets)
> > requires a push-down automaton, which is the usual way to build a
> > parser.
> >
> > It's pretty obvious that a concatenative language without list
> > literals or control structures would be non-parsing. It would also be
> > pretty hard to use, or at least I can't figure out a way to make it
> > easy. I remember when we were talking about simple bases, we came up
> > with the way to build a completely syntax-free language, but although
> > the result was complete, it wasn't at all easy to use.
> we're used to
> [10 20 30]
> but why not simply consider that mere syntactic sugar for the underlying
> functional form
> 10 20 30 3 ,
Or 10 20 pair 30 cons.
That's true, but notice that you used the word "syntactic"? If a
language has syntax, it has to be parsed. You can have sugar if you
want, but as long as you're trying to avoid parsing, you can't have
syntax (at least not in any sense that ties words together).
-Billy
Narcoleptic Electron — 2005-03-06 02:57:54
William Tanksley, Jr wrote:
>
> Stevan Apter wrote:
>
> > we're used to
> > [10 20 30]
> > but why not simply consider that mere
> > syntactic sugar for the underlying
> > functional form
> > 10 20 30 3 ,
>
> Or 10 20 pair 30 cons.
>
Or defining a word "text>list" such that
"[10 20 30]" text>list
would iterate through the list of unicode characters that represents
the initial string "[10 20 30]", and push a new list containing the
numbers 10, 20 and 30. (There are probably plenty of words for this
already... I'm pretty new to this concatenative stuff.) There is
definitely no need, outside of convenience, to represent lists
directly in the language.
But we still have the non-concatenative string enclosure character,
which is a double-quote in my example. I don't think there is a way
to get around this.
I believe that the simplest one can go is allowing only two types of
tokens: words (non-immediate, delimited by whitespace), and strings
(opaque quotations, delimited by double-quotes).
In fact, all of the "special words" used to push numbers on the stack
could also be removed for additional simplicity by defining a word
"text>number" such that
"10" text>number
pushes the number 10 value onto the stack.
I would of course create shorter synonyms for each of the above;
"text>list" could be "[]", and "text>number" could be "#". As such:
"[10 20 30]" [] => pushes list [10 20 30] onto the stack
"10" # => pushes number 10 onto the stack
John Cowan — 2005-03-06 03:54:34
Narcoleptic Electron scripsit:
> I believe that the simplest one can go is allowing only two types of
> tokens: words (non-immediate, delimited by whitespace), and strings
> (opaque quotations, delimited by double-quotes).
This reminds me of Tcl, which is probably the most mainstream
concatenative language (so much so that I don't believe we've
ever discussed it). In Tcl, there are words and there are two
semantically-equivalent syntaxes for strings: those delimited by "...",
which allow C-style escapes, and those delimited by {...}, which do not
but allow nested {...} inside.
--
Si hoc legere scis, nimium eruditionis habes.
Ehrenberg Daniel — 2005-03-06 04:04:54
> This reminds me of Tcl, which is probably the most mainstream
> concatenative language (so much so that I don't believe we've
> ever discussed it). In Tcl, there are words and there are two
> semantically-equivalent syntaxes for strings: those delimited by "...",
> which allow C-style escapes, and those delimited by {...}, which do not
> but allow nested {...} inside.
What are you talking about?! Tcl is not concatenative, it is
applicative. You apply a function to arguments. There is no stack.
Programs cannot just be concatenated. People seem to have gotten way
away from about what a concatenative language is and what parsing is.
Parsing is converting a string into the AST. If the AST is a tolken
list, so be it. Syntax is the textual format. All programming
languages have syntax. If a language has syntactic sugar, it has
syntax. If it's possible to change something about the way programs
are written and it'll change the meaning (for example, swap the
meanings of the ' ' and 'e' characters), then the language has a
syntax. Joy has a syntax. Joy has a parser. Tcl is not concatenative.
Be reasonable, people.
Daniel Ehrenberg
Narcoleptic Electron — 2005-03-06 04:08:53
John Cowan wrote:
> In Tcl, there are words and there are two
> semantically-equivalent syntaxes for strings:
> those delimited by "...",
> which allow C-style escapes, and those delimited
> by {...}, which do not
> but allow nested {...} inside.
There would be no need, that I can see, to allow the latter nestable
strings as a language feature; the string simply represents an opaque
quotation that can have anything inside. For example's sake only, a
programmer could define a "text>list[]" word to parse the string using
square braces within the string as list enclosures, while a
"text>list{}" word could be defined to use curly braces within the
string as enclosures. One could parse a string using whatever list
enclosure character(s) one wanted.
Ultimately, my example language would only have two lexical elements:
code (words) and data (quotations).
Ehrenberg Daniel — 2005-03-06 04:22:43
> Or defining a word "text>list" such that
>
> "[10 20 30]" text>list
>
> would iterate through the list of unicode characters that represents
> the initial string "[10 20 30]", and push a new list containing the
> numbers 10, 20 and 30. (There are probably plenty of words for this
> already... I'm pretty new to this concatenative stuff.) There is
> definitely no need, outside of convenience, to represent lists
> directly in the language.
>
> But we still have the non-concatenative string enclosure character,
> which is a double-quote in my example. I don't think there is a way
> to get around this.
>
> I believe that the simplest one can go is allowing only two types of
> tokens: words (non-immediate, delimited by whitespace), and strings
> (opaque quotations, delimited by double-quotes).
>
> In fact, all of the "special words" used to push numbers on the stack
> could also be removed for additional simplicity by defining a word
> "text>number" such that
>
> "10" text>number
>
> pushes the number 10 value onto the stack.
>
> I would of course create shorter synonyms for each of the above;
> "text>list" could be "[]", and "text>number" could be "#". As such:
>
> "[10 20 30]" [] => pushes list [10 20 30] onto the stack
>
> "10" # => pushes number 10 onto the stack
What's the point of this? All you're doing is moving some of the
parsing to runtime so you can claim that the language has "no syntax".
But it still has a syntax, a syntax that is different than Joy's,
since in Joy, you cannot define a word called []. In Factor's syntax,
you can, but in Factor, we already have a word that takes the place of
[] and #. It's called parse and it takes a string and returns I don't
see how this helps you at all, even in "concatenative theory". And
your "non-concatenative string enclosure character"? WTF? You've
identified the "problem" precisely: there's syntax! This is horrible!
We have to get rid of the syntax! I know how, what if we do
": some \"code\" WITH: complicated ; {{ [[ syn tax ]] }} " eval
then we can have no syntax, just like you wanted. All we have is the
eval word, which isn't syntax. Hurray!
Daniel Ehrenberg
Narcoleptic Electron — 2005-03-06 04:43:46
Daniel Ehrenberg wrote:
> What's the point of this?
A theoretical exercise for the purposes of discussion.
Daniel wrote:
> All you're doing is moving some of the
> parsing to runtime so you can claim that the
> language has "no syntax".
> But it still has a syntax, a syntax that is different
> than Joy's,
> since in Joy, you cannot define a word called [].
Then my syntax has an advantage.
Daniel wrote:
> And your "non-concatenative string enclosure
> character"? WTF? You've
> identified the "problem" precisely: there's syntax!
> This is horrible!
> We have to get rid of the syntax! I know how, what
> if we do
> ": some \"code\" WITH: complicated ; {{ [[ syn tax ]] }} " eval
> then we can have no syntax, just like you wanted.
> All we have is the
> eval word, which isn't syntax. Hurray!
If "eval" is the only word, and the user is not allowed to define new
words, then it is syntax. If the user can define new words, then
there is no need to put all the complexity in the string.
Having "eval" as our only word would be like using a 1-based number
system. 1 = 1, 11 = 2, etc. It doesn't achieve the best balance
between number of digits and number of positions; it has the lowest
number of digits, but the highest number of positions.
I'm trying to find the most efficient balance. For number systems,
this balance is base-3.
My initial email was purely an exercise in trying to boil
concatenative languages down to their most fundamental principles, and
I sent it because I was under the impression that there were people on
this list that were interested in that sort of thing.
John Cowan — 2005-03-06 06:13:55
Ehrenberg Daniel scripsit:
> What are you talking about?! Tcl is not concatenative, it is
> applicative. You apply a function to arguments. There is no stack.
Well, sort of. Applicative languages usually have variables. Tcl
doesn't, except that conventionally certain words make use of them.
And if the "function" word came at the end instead of the beginning of
a line, Tcl would be strictly concatenative.
--
John Cowan
jcowan@... www.ccil.org/~cowan
Female celebrity stalker, on a hot morning in Cairo:
"Imagine, Colonel Lawrence, ninety-two already!"
El Auruns's reply: "Many happy returns of the day!"
Mackenzie Straight — 2005-03-06 13:24:19
On Sun, 6 Mar 2005 01:13:55 -0500, John Cowan <
cowan@...> wrote:
> Well, sort of. Applicative languages usually have variables. Tcl
> doesn't, except that conventionally certain words make use of them.
> And if the "function" word came at the end instead of the beginning of
> a line, Tcl would be strictly concatenative.
I wasn't going to say anything on this thread (because I think it's
perverse and wrong) but this is absolutely, 100% false. Variable
substitution is one of the fundamental primitives of tcl (rule #7 in
man tcl). 'proc' is purely an applicative construct. The language
would not be tcl without variables.
William Tanksley, Jr — 2005-03-06 16:53:48
Narcoleptic Electron <
narcoleptic.electron@...> wrote:
> Or defining a word "text>list" such that
> "[10 20 30]" text>list
> would iterate through the list of unicode characters that represents
> the initial string "[10 20 30]", and push a new list containing the
> numbers 10, 20 and 30. (There are probably plenty of words for this
> already... I'm pretty new to this concatenative stuff.) There is
> definitely no need, outside of convenience, to represent lists
> directly in the language.
That would work, but it's a parser (which is what you're trying to
avoid). So it doesn't save us anything, and in fact adds expense to
the runtime.
-Billy
William Tanksley, Jr — 2005-03-06 16:58:26
John Cowan <
cowan@...> wrote:
> This reminds me of Tcl, which is probably the most mainstream
> concatenative language (so much so that I don't believe we've
> ever discussed it). In Tcl, there are words and there are two
> semantically-equivalent syntaxes for strings: those delimited by "...",
> which allow C-style escapes, and those delimited by {...}, which do not
> but allow nested {...} inside.
Tcl is thoroughly applicative, but its words handle application in an
interesting way -- by manually looking forward in the source. This
definitely relates to the subject of this thread :-).
> Si hoc legere scis, nimium eruditionis habes.
-Billy "Quidquid dicitur latine altum videtur."
Ehrenberg Daniel — 2005-03-06 23:40:49
> Then my syntax has an advantage.
You just admitted that this is a syntax!
>
> If "eval" is the only word, and the user is not allowed to define new
> words, then it is syntax. If the user can define new words, then
> there is no need to put all the complexity in the string.
>
> Having "eval" as our only word would be like using a 1-based number
> system. 1 = 1, 11 = 2, etc. It doesn't achieve the best balance
> between number of digits and number of positions; it has the lowest
> number of digits, but the highest number of positions.
>
> I'm trying to find the most efficient balance. For number systems,
> this balance is base-3.
Did you notice the sarcasm? No matter how much parsing and processing
you try to move to runtime, the syntax isn't getting any less
syntax-like. The syntax still exists. It is completely pointless, no
matter how theory-oriented we are, to move parsing to runtime.
>
> My initial email was purely an exercise in trying to boil
> concatenative languages down to their most fundamental principles, and
> I sent it because I was under the impression that there were people on
> this list that were interested in that sort of thing.
In what way does this boil concatenative languages down to their
fundamental principles?
Daniel Ehrenberg
Narcoleptic Electron — 2005-03-07 04:54:07
William Tanksley, Jr wrote:
> That would work, but it's a parser (which is what you're
> trying to
> avoid).
No I'm not... I'm trying to remove lexical constructs. I have no
problem with parsers.
William Tanksley, Jr wrote:
> So it doesn't save us anything, and in fact
> adds expense to
> the runtime.
Whether it's done dynamically or statically is an implementation
detail as far as I'm concerned. I'm only looking at it from a lexical
simplicity/flexibility standpoint: less punctuation, and no special or
immediate words.
Perhaps I am veering from the thread subject...
Narcoleptic Electron — 2005-03-07 04:56:17
Narcoleptic Electron wrote:
> Whether it's done dynamically or statically is
> an implementation
> detail as far as I'm concerned.
What I mean is that there is nothing that says
"[10 20 30]" []
has to be parsed at runtime; it can be parsed in the same way that
[10 20 30]
is now. Consider it a compiler optimization.
phimvt@lurac.latrobe.edu.au — 2005-03-07 07:59:46
On Sat, 5 Mar 2005, Slava Pestov wrote:
[..]
> Quotations can be optimized. For example, if you compile a word
> definition containing [ * ] map, it generates efficient code by inlining
> the definition of map and "lifting" the quotation to its call site. In
> compiled code, no actual quotations are ever passed around on the stack.
This is at least partially relevant: You know that the Joy scanner
recognises (* and *) as commenting backets, and you might also know that
it recognises the plain parentheses ( and ) as LPAREN and RPAREN. This is
a leftover from the early time when I was designing Joy, when I thought
that sooner or later I would need them for something, I knew not what.
Among the several possibilities was the following:
In Joy programs there are many cases where the parameters are pushed
onto the stack and then immediately consumed:
10 +
In these cases would it not be bettter not to push the 10 at all,
but have a special (optimising) version of + which does not bother
to push but instead really is a unary operator "add-10". This might
be written +(10).
Even more so in the case of most combinators which almost always have
their parameters pushed immediately before they are called. For example,
the ifte-combinator, which needs an if-part [I], a then-part [T] and an
else-part [E]. Most of the time the calls look like this:
[I] [T] [E] ifte
The idea would be to allow three variants of ifte which mean the same
thing:
[I] [T] ifte(E)
[I] ifte(T,E)
ifte(I,T,E)
where the last version would be the most useful, and the other two and the
original are used only in those cases where one or two or three of the
parameters are not known at compile time. The last version would save
three pushes, and it would take its three parameters not from the stack
but directly from the code.
But I abandonded this extra complication because it would increase
the size of the interpreter substantially: every unary oper/combin-ator
would need two versions, every binary one would need three, every ternary
would need four, every quaternary would need five.
The same would be true if the alternative syntax is not used explicitly
by the programmer. It could be left to the compiler to recognise the
many special cases where some or all of the parameters are known at
compile time and the pushes could be avoided.
So I concluded that the huge increase in the size of the interpreter would
not warrant this optimisation. At any rate, plain left and right
parentheses ( and ) are still available for some extension of Joy.
However, if I understand you correctly, you are doing something rather
different for your combinators: essentially inlining the code for the
quotation into the code for the map combinator (and presumably something
analogous for the other combinators). That will save the call to the
quotation, which could be significant if the quotation itself is rather
short. But what do you do in those (admittedly few) cases where the
quotation parameters for a combinator are not all known at compile time?
In my Joy programs I quite often use what I referred to as "constructed"
programs. Do you then have several versions in the interpreter or
compiler to handle this? Does it increase the size of the implementation?
- Manfred
Slava Pestov — 2005-03-07 15:27:04
phimvt@... wrote:
> In Joy programs there are many cases where the parameters are pushed
> onto the stack and then immediately consumed:
> 10 +
> In these cases would it not be bettter not to push the 10 at all,
> but have a special (optimising) version of + which does not bother
> to push but instead really is a unary operator "add-10". This might
> be written +(10).
I use this trick to optimize several words in the compiler.
> Even more so in the case of most combinators which almost always have
> their parameters pushed immediately before they are called. For example,
> the ifte-combinator, which needs an if-part [I], a then-part [T] and an
> else-part [E]. Most of the time the calls look like this:
> [I] [T] [E] ifte
> The idea would be to allow three variants of ifte which mean the same
> thing:
> [I] [T] ifte(E)
> [I] ifte(T,E)
> ifte(I,T,E)
> where the last version would be the most useful, and the other two and the
> original are used only in those cases where one or two or three of the
> parameters are not known at compile time. The last version would save
> three pushes, and it would take its three parameters not from the stack
> but directly from the code.
This is what the Factor compiler does. No quotations are pushed or
shuffled in compiled code.
> However, if I understand you correctly, you are doing something rather
> different for your combinators: essentially inlining the code for the
> quotation into the code for the map combinator (and presumably something
> analogous for the other combinators). That will save the call to the
> quotation, which could be significant if the quotation itself is rather
> short. But what do you do in those (admittedly few) cases where the
> quotation parameters for a combinator are not all known at compile time?
I don't compile the code in that case, and it runs in the interpreter.
Slava
William Tanksley, Jr — 2005-03-07 16:15:23
Narcoleptic Electron <
narcoleptic.electron@...> wrote:
> Narcoleptic Electron wrote:
> > Whether it's done dynamically or statically is
> > an implementation detail as far as I'm concerned.
> What I mean is that there is nothing that says
> "[10 20 30]" []
> has to be parsed at runtime; it can be parsed in the same way that
> [10 20 30]
> is now. Consider it a compiler optimization.
If it's parsed in the same way, then how is it an optimization? Its
syntax is certainly more complicated, since it requires parsing all
the same symbols that Joy requires, plus parsing a string, plus
recognising the [] word.
Plus, it has a distinct disadvantage that it *appears* to be a normal
concatenative pair of words, which means that for example the
following should be possible: "[10 20 30]" 10 swap []. In this case a
simple optimiser is needed to see that the two belong together -- but
what if some programmer attempts to factor the list out into another
word?
-Billy
William Tanksley, Jr — 2005-03-07 16:51:59
On Sat, 5 Mar 2005 23:04:54 -0500, Ehrenberg Daniel <
microdan@...> wrote:
> > This reminds me of Tcl, which is probably the most mainstream
> > concatenative language (so much so that I don't believe we've
> What are you talking about?! Tcl is not concatenative, it is
> applicative. You apply a function to arguments. There is no stack.
> Programs cannot just be concatenated.
I agree.
> People seem to have gotten way
> away from [...] what parsing is. Parsing is converting a string into the AST.
> If the AST is a tolken list, so be it.
The AST? That would be the _A_bstract _S_yntax _L_ist? There's a
reason it's called an AST. A hypothetical concatenative languages
wouldn't need it (see Kerby's "Theory of Concatenative Combinators").
Real ones do; but every time we use one a little bit of
concatenativity is lost, so we try in various ways to minimise the
use. In Forth, the only syntax trees appear in parsing immediate words
(which conventionally limit themselves to ONLY parsing the very next
token from the stream) and structured programming constructs (which
are conventionally only used one-per-definition). Both conventions
minimise the complexity of the tree in each word, and maximise the
amount of source that's actually concatenative.
> Syntax is the textual format.
I'm not breaking any conventions by referring to concatenative
languages as needing "less syntax" than applicative languages. It's a
standard way of speaking. Lisp derivatives speak of needing less
syntax as well.
> All programming
> languages have syntax. If a language has syntactic sugar, it has
> syntax. If it's possible to change something about the way programs
> are written and it'll change the meaning (for example, swap the
> meanings of the ' ' and 'e' characters), then the language has a
> syntax. Joy has a syntax. Joy has a parser. Tcl is not concatenative.
Joy definitely has a parser and syntax. It's very explicit. Factor has
a parser too, more distributed but equally explicit. Forth has a much
less explicit parser (it took some arguing by slava to convince me
that it has one at all).
> Daniel Ehrenberg
-Billy
Slava Pestov — 2005-03-08 03:08:36
William Tanksley, Jr wrote:
> Joy definitely has a parser and syntax. It's very explicit. Factor has
> a parser too, more distributed but equally explicit. Forth has a much
> less explicit parser (it took some arguing by slava to convince me
> that it has one at all).
You consistently miss the point that the Factor parser is *identical* to
the Forth parser. How can Forth have a 'less explicit' parser?
Slava
William Tanksley, Jr — 2005-03-08 04:39:52
Slava Pestov <
slava@...> wrote:
> William Tanksley, Jr wrote:
> > Joy definitely has a parser and syntax. It's very explicit. Factor has
> > a parser too, more distributed but equally explicit. Forth has a much
> > less explicit parser (it took some arguing by slava to convince me
> > that it has one at all).
> You consistently miss the point that the Factor parser is *identical* to
> the Forth parser. How can Forth have a 'less explicit' parser?
First of all, don't miss that I now agree with you -- you convinced me
that Forth as a language MUST be parsed in order to match 'if' and
'then' (and all the other structured control words).
Second, I don't know any Factor, but didn't you just say that Factor
had an explicitly defined parse structure? Forth doesn't. That's
"explicit" in my book.
> Slava
-Billy
Narcoleptic Electron — 2005-03-07 17:17:20
William Tanksley, Jr wrote:
> Plus, it has a distinct disadvantage that it *appears* to be a normal
> concatenative pair of words
But it _is_ a normal concatenative pair of words...
William Tanksley, Jr wrote:
> which means that for example the
> following should be possible: "[10 20 30]" 10 swap [].
Yes. This would swap the string "[10 20 30]" with 10 on the stack,
then convert the string "[10 20 30]" to a list. This would certainly
incur runtime overhead; so would doing the same in any other language.
William Tanksley, Jr wrote:
> what if some programmer attempts to factor the list out into another
> word?
They would do this first:
"[10 20 30]" []
Then they would factor the resulting list value on the stack to another word.
I still see no reason for direct lexical support for list literals in
my little language.
Ehrenberg Daniel — 2005-03-08 02:46:54
> > People seem to have gotten way
> > away from [...] what parsing is. Parsing is converting a string into the AST.
> > If the AST is a tolken list, so be it.
>
> The AST? That would be the _A_bstract _S_yntax _L_ist? There's a
> reason it's called an AST. A hypothetical concatenative languages
> wouldn't need it (see Kerby's "Theory of Concatenative Combinators").
> Real ones do; but every time we use one a little bit of
> concatenativity is lost, so we try in various ways to minimise the
> use. In Forth, the only syntax trees appear in parsing immediate words
> (which conventionally limit themselves to ONLY parsing the very next
> token from the stream) and structured programming constructs (which
> are conventionally only used one-per-definition). Both conventions
> minimise the complexity of the tree in each word, and maximise the
> amount of source that's actually concatenative.
>
Those concatenative combinators as stated would need trees to hold the
quotations, wouldn't they? If you have a list within the syntax list,
isn't it a tree? People have made some pretty weird suggestions
recently about having [ and ] be runtime words, or the "," word, but I
think these would only make concatenative theory more complicated. Ok,
it can be done without a tree, but in theory or in practice, would we
ever want it to be done without a tree?
> > Syntax is the textual format.
>
> I'm not breaking any conventions by referring to concatenative
> languages as needing "less syntax" than applicative languages. It's a
> standard way of speaking. Lisp derivatives speak of needing less
> syntax as well.
I've never heard that before. I've heard "simpler syntax", and I think
that applies very well here. Maybe people do say "less syntax", but it
sounds kind of weird to me.
>
> > All programming
> > languages have syntax. If a language has syntactic sugar, it has
> > syntax. If it's possible to change something about the way programs
> > are written and it'll change the meaning (for example, swap the
> > meanings of the ' ' and 'e' characters), then the language has a
> > syntax. Joy has a syntax. Joy has a parser. Tcl is not concatenative.
>
> Joy definitely has a parser and syntax. It's very explicit. Factor has
> a parser too, more distributed but equally explicit. Forth has a much
> less explicit parser (it took some arguing by slava to convince me
> that it has one at all).
Forth's and Factor's parsers work really similarly, the only big
difference being the output format.
Daniel Ehrenberg
Mackenzie Straight — 2005-03-08 10:32:40
On Mon, 7 Mar 2005 20:39:52 -0800, William Tanksley, Jr
<
wtanksleyjr@...> wrote:
> Second, I don't know any Factor, but didn't you just say that Factor
> had an explicitly defined parse structure? Forth doesn't. That's
> "explicit" in my book.
>
: parse-loop ( -- )
scan-word [
dup parsing? [ execute ] [ swons ] ifte parse-loop
] when* ;
> -Billy
Mackenzie Straight — 2005-03-08 10:48:03
On Mon, 7 Mar 2005 08:51:59 -0800, William Tanksley, Jr
<
wtanksleyjr@...> wrote:
> The AST? That would be the _A_bstract _S_yntax _L_ist? There's a
> reason it's called an AST. A hypothetical concatenative languages
> wouldn't need it (see Kerby's "Theory of Concatenative Combinators").
> Real ones do; but every time we use one a little bit of
> concatenativity is lost, so we try in various ways to minimise the
> use.
Why is "a little bit of concatenativity lost?" If this is the case,
you need to stop playing parser games at once and fix your theory.
BEGIN DEFINITION A000-SUM-SQUARES.
PUSH INTEGER 0.
BEGIN QUOTATION.
PERFORM DUPLICATE.
PERFORM MULTIPLY.
PERFORM ADD.
END QUOTATION.
PERFORM FOLD.
END DEFINITION.
Who cares? You should not be thinking about concrete syntax.
> -Billy
William Tanksley, Jr — 2005-03-08 15:52:47
Ehrenberg Daniel <
microdan@...> wrote:
> > > People seem to have gotten way
> > > away from [...] what parsing is. Parsing is converting a string into the AST.
> > > If the AST is a tolken list, so be it.
> > The AST? That would be the _A_bstract _S_yntax _L_ist? There's a
> > reason it's called an AST. A hypothetical concatenative languages
> > wouldn't need it (see Kerby's "Theory of Concatenative Combinators").
> > Real ones do; but every time we use one a little bit of
> > concatenativity is lost, so we try in various ways to minimise the
> > use. In Forth, the only syntax trees appear in parsing immediate words
> > (which conventionally limit themselves to ONLY parsing the very next
> > token from the stream) and structured programming constructs (which
> > are conventionally only used one-per-definition). Both conventions
> > minimise the complexity of the tree in each word, and maximise the
> > amount of source that's actually concatenative.
> Those concatenative combinators as stated would need trees to hold the
> quotations, wouldn't they? If you have a list within the syntax list,
> isn't it a tree?
Forth doesn't have quotations or literal lists.
> People have made some pretty weird suggestions
> recently about having [ and ] be runtime words,
This is not a "suggestion"; it's how Forth and Factor work (although,
of course, Forth's meaning and Factor's meaning for those words are
very different).
> or the "," word, but I
> think these would only make concatenative theory more complicated.
This is true. Immediate words in specific do not mesh at all with
concatenative theory. The ideas are foreign. On the other hand,
parsing does not mesh with concatenative theory, and you're advocating
a full use of parsing. So it's not clear...
> Ok,
> it can be done without a tree, but in theory or in practice, would we
> ever want it to be done without a tree?
Forth doesn't use a tree. That's about as "in practice" as it gets --
Forth is the only concatenative language that's ever achieved (brief)
success as a programmer's tool (Postscript deserves kudos, but it
didn't ever try to be a programmer's tool).
> > > All programming
> > > languages have syntax. If a language has syntactic sugar, it has
> > > syntax. If it's possible to change something about the way programs
> > > are written and it'll change the meaning (for example, swap the
> > > meanings of the ' ' and 'e' characters), then the language has a
> > > syntax. Joy has a syntax. Joy has a parser. Tcl is not concatenative.
> > Joy definitely has a parser and syntax. It's very explicit. Factor has
> > a parser too, more distributed but equally explicit. Forth has a much
> > less explicit parser (it took some arguing by slava to convince me
> > that it has one at all).
> Forth's and Factor's parsers work really similarly, the only big
> difference being the output format.
Right; Factor has a defined one, while Forth doesn't, but rather
depends on behavioral interactions (for example, "IF" is defined to
drop an <if-sys> on the control stack at compile time, and "ELSE" is
defined to consume one <if-sys> and produce another one. "THEN", of
course, consumes an <if-sys>. Other control structures share the
<if-sys>; technically, an <if-sys> is a resolution address for a
forward jump).
> Daniel Ehrenberg
-Billy
Slava Pestov — 2005-03-08 21:25:25
William Tanksley, Jr wrote:
> Second, I don't know any Factor, but didn't you just say that Factor
> had an explicitly defined parse structure? Forth doesn't. That's
> "explicit" in my book.
I'm still not sure what you mean. Both Forth and Factor parse code and
produce some kind of structure as a result. Lets assume for the sake of
argument that a direct threaded Forth is used. Forth produces threaded
code in the dictionary; Factor produces a list.
The higher-level code to produce both looks exactly the same.
The only difference is that in Factor, the list is built on the data
stack, and in Forth, the , word is used to append threads to the dictionary.
Slava
William Tanksley, Jr — 2005-03-08 23:17:32
Slava Pestov <
slava@...> wrote:
> William Tanksley, Jr wrote:
> > Second, I don't know any Factor, but didn't you just say that Factor
> > had an explicitly defined parse structure? Forth doesn't. That's
> > "explicit" in my book.
> I'm still not sure what you mean. Both Forth and Factor parse code and
> produce some kind of structure as a result.
But Forth doesn't promise anything of the sort. The only result Forth
promises to produce is the specified semantics.
> Lets assume for the sake of argument that a direct threaded
> Forth is used.
This is a bigger assumption than I'm willing to quickly make -- there
are many other types of Forth. A direct threaded Forth, of all Forths,
looks the most like your parser, so I can see why you'd want to use it
as an example. An interprocedurally optimized native code Forth looks
very different from your example.
With that caveat aside, let's use your assumption of a direct-threaded Forth.
> Forth produces threaded code in the dictionary; Factor produces a list.
Nothing manipulates or accesses threaded code in the dictionary. Its
format is undefined and unknown. Many compilers do pinhole
optimization, replacing common series of opcodes with faster
operations, so there's no 1-1 mapping between words in the source and
threads in the dictionary. Some compilers don't even generate code
until after the source is loaded; they dynamically generate a dataflow
graph, reduce it, and only then generate code.
> The higher-level code to produce both looks exactly the same.
Then why did you define a parse tree? Aren't Factor's lists (or trees)
are defined in order to allow active manipulation by words in the
source?
> The only difference is that in Factor, the list is built on the data
> stack, and in Forth, the , word is used to append threads to the dictionary.
No; the "," word in Forth doesn't manipulate threads. It only allots
and stores a single integer. A thread may or may not be different.
In Forth, if you want to manipulate code, you do so at the source
level, with plain text. This works brilliantly because concatenative
source maps so simply onto concatenative semantics.
Now, your system in Factor has a lot of strengths that Forth doesn't
have. Like Joy, Factor words can take apart and put back together
quotations, while text processing is much more more difficult burden
that Forth has to bear. Factor, like Joy, is a nice language.
Forth makes do without those advantages, with the reasoning/excuse
that their full power is almost never useful, and Forth's system
provides most if not all of the useful functionality.
One example of missing functionality in Forth, by the way, is static
binding of macro code. It's very easy to make macros insert dynamicly
bound code, but often you want a macro to contain code that has
predictable behavior. This is needlessly hard in Forth.
> Slava
-Billy
William Tanksley, Jr — 2005-03-08 16:36:08
Narcoleptic Electron <
narcoleptic.electron@...> wrote:
> William Tanksley, Jr wrote:
> > Plus, it has a distinct disadvantage that it *appears* to be a normal
> > concatenative pair of words
> But it _is_ a normal concatenative pair of words...
You just said that it's not -- that it's evaluated at compile time.
> William Tanksley, Jr wrote:
> > which means that for example the
> > following should be possible: "[10 20 30]" 10 swap [].
> Yes. This would swap the string "[10 20 30]" with 10 on the stack,
> then convert the string "[10 20 30]" to a list. This would certainly
> incur runtime overhead; so would doing the same in any other language.
But most other languages don't need to convert back and forth between
strings and lists merely in order to express a list. (Tcl excluded.)
> William Tanksley, Jr wrote:
> > what if some programmer attempts to factor the list out into another
> > word?
> They would do this first:
> "[10 20 30]" []
> Then they would factor the resulting list value on the stack to another word.
I don't think you understand what "factoring" is. Factoring is when
you take a definition and split it textually into two parts, each one
with its own definition. There's nothing about a "resulting list value
on the stack"; factoring refers only to the plain text of the
definition.
> I still see no reason for direct lexical support for list literals in
> my little language.
The fact is that you're providing full lexical support for list
literals; you're just hiding it inside a very complex string system,
and then defining a bunch of optimization rules that don't always
work. I really don't see the point.
Keep it simple: if you want to be able to include inline list
literals, just do it. If you don't want to pay the price of doing it,
then don't support inline list literals. Factor and Joy gladly pay the
price; Forth doesn't. There is no "third way".
If you do want lists, you can have them without having inline list
literals -- use list construction words like cons and concat, and
perhaps provide a minilanguage (doing nothing but populating a list)
to be used outside of definitions. True, the minilanguage will be
non-concatenative, but it'll be purely declarative, so reasoning about
it will be simple.
I'm coming more and more to dislike quotations as they're used In Joy;
they make it very easy to do very uncommon things such as list
manipulation of arbitrary code, and the result is that it's not always
easy to tell how to optimize a quotation. Most of the time quotations
are used, a function-address could have been used instead (fully
optimised); when you actually need to construct a word at runtime,
full access to the compiler would seem to be more helpful.
-Billy
Mackenzie Straight — 2005-03-08 20:19:31
On Tue, 8 Mar 2005 07:52:47 -0800, William Tanksley, Jr
<
wtanksleyjr@...> wrote:
> Right; Factor has a defined one, while Forth doesn't, but rather
> depends on behavioral interactions (for example, "IF" is defined to
> drop an <if-sys> on the control stack at compile time, and "ELSE" is
> defined to consume one <if-sys> and produce another one. "THEN", of
> course, consumes an <if-sys>. Other control structures share the
> <if-sys>; technically, an <if-sys> is a resolution address for a
> forward jump).
This is (and we've been trying to show you this for some time now) the
_exact_ same way the Factor parser works: probably the simplest
example is the definitions of [ and ]. [ simply pushes a list onto the
data stack at parse time. Non-parsing words always get consed into the
list on top of the stack (much the same way as non-immediate words are
handled in Forth, the only major difference is that we use a different
data structure). ], then, simply reverses the list on top of the stack
and conses it onto the next list down.
Another example is the ; word. All ; does is call a quotation on the
stack (well, it also updates a state variable but this is not
important). Then we have a family of definition-style words that use
this: colon definitions, M:, C:, USING:, PREDICATE: and so on. None of
this is different, more or less explicit than the Forth way. They are
the same thing.
If you're not convinced, this is working Factor code:
[ define-compound swap CREATE [ 2 2 + . ; foobar
> -Billy
Narcoleptic Electron — 2005-03-09 17:10:33
William Tanksley, Jr wrote:
> Narcoleptic Electron <narcoleptic.electron@...> wrote:
> > William Tanksley, Jr wrote:
> > > Plus, it has a distinct disadvantage that it *appears* to be a normal
> > > concatenative pair of words
>
> > But it _is_ a normal concatenative pair of words...
>
> You just said that it's not -- that it's evaluated at compile time.
I very well may have an incomplete understanding, but I think you are
tying the lexical details with the implementation details more than
necessary.
This is how I envision the tokenization and substitution phase in my
language to work:
- Tokenize the next word in the source code string: get the word
string, and put it into the "code queue".
- If there is a substitution defined for the word, substitute it and
recursively tokenize and substitute the resulting string.
- Tokenize the next word in the source code string.
- Etc.
At the end of this phase, the "code queue" will contain "atomic
combinator" word strings.
Now, instead of waiting until runtime to actually execute this thing,
we execute it now. It would operate on a now-empty "data stack".
When a program is popped from the code queue for which there aren't
enough inputs on the data stack, the program is pushed on to the data
stack as a "curried program" value.
At the end of this phase, the data stack would contain values, some of
which are curried program values, and all of which have an internal
structure that is normally unknown to the programmer.
Then, at runtime, the program inputs come from the "input stream".
Here's how it plays out:
- Tokenize the next string value in the input stream, and add it to
the bottom of the data stack.
- Check the first available curried program (closest to the bottom of
the data stack); if it now has enough data items under it, run it.
Then check the next one, etc., until we find one without sufficient
inputs.
- Tokenize the next string value in the input stream, and add it to
the bottom of the data stack, etc.
At the end of this phase, the data stack would contain values, some of
which may still be curried program values, and all of which have an
internal structure that is normally unknown to the programmer.
It is sort of a continuation-based approach. Note that all data
coming in from the input stream is in a string format; this is why the
only "value" type in the language is a string; every other value must
come from a string somehow.
Is there any implementation out there that operates in this way?
Would it even work? It seems pretty simple to me, but it may have
flaws, as this whole thing is just off the top of my head.
William Tanksley, Jr wrote:
> Narcoleptic Electron <narcoleptic.electron@...> wrote:
> > William Tanksley, Jr wrote:
> > > which means that for example the
> > > following should be possible: "[10 20 30]" 10 swap [].
>
> > Yes. This would swap the string "[10 20 30]" with 10 on the stack,
> > then convert the string "[10 20 30]" to a list. This would certainly
> > incur runtime overhead; so would doing the same in any other language.
>
> But most other languages don't need to convert back and forth between
> strings and lists merely in order to express a list. (Tcl excluded.)
You don't need to convert back and forth. But you can if you want.
William Tanksley, Jr wrote:
> Narcoleptic Electron <narcoleptic.electron@...> wrote:
> > William Tanksley, Jr wrote:
> > > what if some programmer attempts to factor the list out into another
> > > word?
>
> > They would do this first:
> > "[10 20 30]" []
> > Then they would factor the resulting list value on the stack to another word.
>
> I don't think you understand what "factoring" is. Factoring is when
> you take a definition and split it textually into two parts, each one
> with its own definition. There's nothing about a "resulting list value
> on the stack"; factoring refers only to the plain text of the
> definition.
I read the term "factoring" in the general programming sense, as
opposed to your domain-specific definition, which is equally valid.
I am fairly new to all these languages, so I am unfamiliar with many
details. I was assuming that there was some way to do dynamic
assignment of values on the stack to words.
To do static assignment to words (i.e. assign just the text to a word,
for substitution only), one would assign
"[10 20 30]" []
to a word, for substitution. Surely you could see that without my
explanation here; have I missed something?
Thanks for taking the time to explain; you are far more knowledgable
on this subject than I. I'm still learning the concatenative basics.
William Tanksley, Jr — 2005-03-09 18:46:15
Mackenzie Straight <
eizneckam@...> wrote:
> On Tue, 8 Mar 2005 07:52:47 -0800, William Tanksley, Jr
> <wtanksleyjr@...> wrote:
> > Right; Factor has a defined one, while Forth doesn't, but rather
> > depends on behavioral interactions (for example, "IF" is defined to
> This is (and we've been trying to show you this for some time now) the
> _exact_ same way the Factor parser works: probably the simplest
> example is the definitions of [ and ]. [ simply pushes a list onto the
> data stack at parse time. Non-parsing words always get consed into the
> list on top of the stack (much the same way as non-immediate words are
> handled in Forth, the only major difference is that we use a different
> data structure). ], then, simply reverses the list on top of the stack
> and conses it onto the next list down.
That, by the way, is a beautiful semantics.
And I'm not arguing (anymore) with how Forth vs Factor works; it's
quite clear to me (thanks to all your explanations) that Forth and
Factor are both based on the same very simple core.
I'm just pointing out that unlike Forth, the standard defintion of
Factor includes specific, detailed information about how those
immediate words interact in order to build up a definition. In Forth
the ONLY defined interactions are between words that must interact.
For example, IF must interact with a currently compiling definition;
so its behavior is undefined interactively. THEN must receive an
<if-sys> on the compile stack, so its behavior is undefined otherwise.
ELSE is defined only when both IF and THEN would be defined.
That's literally _all_ you know about them, aside from the trivial
details of the runtime behavior that they create :-). And those are
probably the best-defined immediate words in the entire Forth
standard.
Factor's parsing words combine to produce a defined, documented
structure that can be manipulated after the fact by other words.
Forth's parsing words do _not_.
> Another example is the ; word. All ; does is call a quotation on the
> stack (well, it also updates a state variable but this is not
> important). Then we have a family of definition-style words that use
> this: colon definitions, M:, C:, USING:, PREDICATE: and so on. None of
> this is different, more or less explicit than the Forth way. They are
> the same thing.
I think our only disagreement is what I mean when I say "explicit". I
mean that Forth's parse structure -- the result of running the parser
over source code -- is completely undocumented, undefined, and
therefore not explicit.
-Billy
sa@dfa.com — 2005-03-09 19:13:22
phimvt@lurac.latrobe.edu.au — 2005-03-11 04:14:33
On Mon, 7 Mar 2005, Slava Pestov wrote:
[.. answers to several of my questions ..]
Thank you for your answers.
Let me use this opportunity to say how very impressed I am with
your work. I said this before, some months ago, but it bears saying
again.
Keep it up.
- Manfred
phimvt@lurac.latrobe.edu.au — 2005-03-11 05:43:51
On Wed, 9 Mar 2005, Narcoleptic Electron wrote:
[..]
> This is how I envision the tokenization and substitution phase in my
> language to work:
> - Tokenize the next word in the source code string: get the word
> string, and put it into the "code queue".
> - If there is a substitution defined for the word, substitute it and
> recursively tokenize and substitute the resulting string.
> - Tokenize the next word in the source code string.
> - Etc.
>
> At the end of this phase, the "code queue" will contain "atomic
> combinator" word strings.
>
> Now, instead of waiting until runtime to actually execute this thing,
> we execute it now. It would operate on a now-empty "data stack".
> When a program is popped from the code queue for which there aren't
> enough inputs on the data stack, the program is pushed on to the data
> stack as a "curried program" value.
>
> At the end of this phase, the data stack would contain values, some of
> which are curried program values, and all of which have an internal
> structure that is normally unknown to the programmer.
>
> Then, at runtime, the program inputs come from the "input stream".
> Here's how it plays out:
> - Tokenize the next string value in the input stream, and add it to
> the bottom of the data stack.
> - Check the first available curried program (closest to the bottom of
> the data stack); if it now has enough data items under it, run it.
> Then check the next one, etc., until we find one without sufficient
> inputs.
> - Tokenize the next string value in the input stream, and add it to
> the bottom of the data stack, etc.
>
> At the end of this phase, the data stack would contain values, some of
> which may still be curried program values, and all of which have an
> internal structure that is normally unknown to the programmer.
>
> It is sort of a continuation-based approach. Note that all data
> coming in from the input stream is in a string format; this is why the
> only "value" type in the language is a string; every other value must
> come from a string somehow.
>
> Is there any implementation out there that operates in this way?
> Would it even work? It seems pretty simple to me, but it may have
> flaws, as this whole thing is just off the top of my head.
I once toyed with an idea that seems similar to what you are doing.
It works in plain Joy:
[*] first 2 3 + 10 # several pushes, but do first and + now.
stack # push the stack as a quotation [10 5 *]
[[] unstack] dip # remove everything below top qotation.
i . # execute quotation, doing * .
50 # that's the answer.
Note that first and + were done in phase 1, and * in phase 2.
In this way of doing it,in phase 1 one has to think prefix for phase 2.
If that is bothersome, could do a reverse before the final i.
This seems to be the closest I can get for using Joy as its own
macro processor in phase 1 in preparation for "real thing" in
phase 2.
But I cannot at this stage think of much use for it. Anyone ?
- Manfred
William Tanksley, Jr — 2005-03-11 19:34:37
Narcoleptic Electron <
narcoleptic.electron@...> wrote:
> William Tanksley, Jr wrote:
> > Narcoleptic Electron <narcoleptic.electron@...> wrote:
> > > William Tanksley, Jr wrote:
> > > > Plus, it has a distinct disadvantage that it *appears* to be a normal
> > > > concatenative pair of words
> > > But it _is_ a normal concatenative pair of words...
> > You just said that it's not -- that it's evaluated at compile time.
> I very well may have an incomplete understanding, but I think you are
> tying the lexical details with the implementation details more than
> necessary.
No, my misunderstanding -- I thought you were saying that every [] HAD
to be preceded by a list-string. Since that's not true, your language
is concatenative.
Let's discuss your language without optimization for now, under the
principle that optimization is best done after we get something
working. I'll skip all of your optimization details; my apologies,
because they look very clever. You should definitely work them out, or
if you'd like just tell me to go back and look at them and I'll start
a new thread to discuss them :-).
> Note that all data
> coming in from the input stream is in a string format; this is why the
> only "value" type in the language is a string; every other value must
> come from a string somehow.
This implies that every parsed structure is entirely contained in a
string, which cannot be correct. If it were correct, you would have to
have nested strings {}, but such things would require a parser of
their own; therefore there would be a parsed entity that isn't
contained in a string, therefore violating the assumption that every
parsed structure is contained in a string.
Therefore we conclude that some parsed structures are not kept in
strings; so now you need to answer why these ones in particular _are_.
The problems with requiring list parsing to be supported at runtime
are obvious:
- runtime errors
- runtime slowness
- runtime library size.
The problems with hiding list parsing inside strings are:
- an additional set of characters around lists (string delimiters)
- an additional word needed to read lists, most of the time appearing
right after the list
- difficulty in visually telling what type a string is going to be
Is there some advantage? If so, I'm missing it. The only advantage
you've claimed is that the resulting language has only one syntactic
datatype: string. The problem is that your language has a large number
of actual datatypes, and in practice they all have to be known in
order to read or write programs.
> Would it even work? It seems pretty simple to me, but it may have
> flaws, as this whole thing is just off the top of my head.
I haven't dug through your optimization technique, but it appears
similar to some other things we've discussed before. I think I called
it "aggressive evaluation", almost the opposite of the "lazy
evaluation" practiced by applicative languages :-). Keep looking at it
-- I suspect it's a good thing to study.
> > But most other languages don't need to convert back and forth between
> > strings and lists merely in order to express a list. (Tcl excluded.)
> You don't need to convert back and forth. But you can if you want.
If you don't ever convert, then how are you going to use the list
value stored in the string?
> > I don't think you understand what "factoring" is. Factoring is when
> > you take a definition and split it textually into two parts, each one
> > with its own definition. There's nothing about a "resulting list value
> > on the stack"; factoring refers only to the plain text of the
> > definition.
> I read the term "factoring" in the general programming sense, as
> opposed to your domain-specific definition, which is equally valid.
What is the "general programming sense" of "factor"? I've only seen
"factor" used as the obvious mathematical definition, and as the
definition I used (which is closely related to the mathematical one).
> I am fairly new to all these languages, so I am unfamiliar with many
> details. I was assuming that there was some way to do dynamic
> assignment of values on the stack to words.
Do you mean something special when you say "words"? I'm going to
assume you mean something like "variables". Most concatenative
languages don't allow mutable variables, but Forth does, and yours can
if you want.
> To do static assignment to words (i.e. assign just the text to a word,
> for substitution only), one would assign
> "[10 20 30]" []
> to a word, for substitution.
I think "static assignment" is something like a function or constant
definition, right?
> Surely you could see that without my
> explanation here; have I missed something?
Well, I'm not sure. If I read you right, this idea puts a lot of
visual clutter in the language itself, and a lot of complexity into
either the optimizer, the runtime, or both. I don't see the advantage.
-Billy
Narcoleptic Electron — 2005-03-14 18:14:26
Thanks for the feedback and encouragement, Billy and Manfred. I've
decided to flesh out some of my ideas a bit more before continuing the
discussion. Hopefully within the next couple days I'll get some time
to respond to your messages fully.
One thing that I have decided, however, is that the strings in my
syntax will be enclosed with square brackets, rather than double
quotes, and unbalanced brackets within need to be escaped. This
allows lists to be expressed as strings more naturally, making
construction of a list from a string easier.
Strings will be the only "literal" in the language; every other token is a word.
William Tanksley, Jr — 2005-03-15 03:44:34
Narcoleptic Electron <
narcoleptic.electron@...> wrote:
> discussion. Hopefully within the next couple days I'll get some time
> to respond to your messages fully.
Cool! Good luck.
> One thing that I have decided, however, is that the strings in my
> syntax will be enclosed with square brackets, rather than double
> quotes, and unbalanced brackets within need to be escaped. This
> allows lists to be expressed as strings more naturally, making
> construction of a list from a string easier.
I think that's a great choice, considering what you want.
> Strings will be the only "literal" in the language; every other token is a word.
I'm not certain if your goals are right yet (I await more
understanding!), but your design will more effectively reach them :-).
-Billy
icpdesign — 2005-09-21 15:56:10
When I read about string shuffling in previous messages I recalled
the proposition of Brent Kerby to add lambda (\x not exactly lambda,
as noted by Dr Manfred, i think, in the sense that there is no
formal parameters). I found the idea great, because we may define
most of combinators using this \x construct.
This looks very much as the string shuffling proposed by Dr Manfred,
but i found the \x less intrusive (no colon and no parens).
Quadratic problem solution using string shuffling:
quad == (a b c : b b * 4 a c * * - 2 a * /)
3 1 13 quad
using \x
quad = \a \b \c b b * 4 a c * * - 2 a * /
3 1 13 quad
I propose the following construct:
#n == replace the holded place in the quotation by the nth element
of the stack; the nth element of the stack is zaped
example:
A B C [#1 #3 #1] => B [C A C]
using this construct we can solve the quadratic as follows:
quad == [#1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /] i
3 1 13 quad
Thank you for the this Joy (#Joy)
Taoufik Dachraoui
--- In
concatenative@yahoogroups.com, "William Tanksley, Jr"
<wtanksleyjr@g...> wrote:
> Slava Pestov <slava@j...> wrote:
> > This is *exactly* how the Factor parser works. It reads tokens
one at a
> > time, and looks them up in the dictionary. If the token
corresponds to a
> > parsing word, it is executed immediately. The only difference is
that
> > instead of calling COMPILE, or POSTPONE LITERAL it just appends
words
> > and numbers as they are read to a list. So the output is not
compiled
> > tokens in the dictionary, but a list (with possibly nested
sublists).
>
> In other words, Factor (1) has a defined dictionary format and (2)
> uses lists to store the contents of the dictionary. Forth doesn't
have
> a defined format, and uses arrays. (You could, I assume, have used
the
> names COMPILE, and POSTPONE LITERAL.)
>
> > > I fundamentally do not agree with the concept of building a
> > > concatenative language that requires a parser. I fundamentally
do
> > > agree with providing a parser and using it for complex forms,
such as
> > > literal lists.
>
> > In Factor, lists are read using the [ and ] immediate words.
>
> Yes, I've seen a similar thing done for Forth, although of course
the
> result isn't as pleasant as Factor. As much as I like Forth, there
are
> many excellent things it doesn't do.
>
> > Similarly, all non-concatenative aspects of syntax, such as
the : and ;
> > words that delimit word definitions, are immediate words.
>
> Yup, makes sense.
>
> > So the difference between Factor and Forth is that in Forth, the
parser
> > generates executable code directly. In Factor, code generation is
> > decoupled from parsing, which gives us a superior multi-pass
compiler.
>
> Actually, the only difference I see is that Factor defines the
parse
> tree and gives the program access to it. Forth doesn't. This gives
a
> Factor program the ability to do things that a Forth program can't,
> but doesn't give any optimization advantages. There's nothing
> preventing Forth from being parsed into an intermediate format --
in
> fact, unlike Factor it could be parsed into some other format
(perhaps
> a dataflow format rather than a stack VM). As I'm sure you know,
there
> are good Forth optimisers.
>
> But I don't want to try to give a commercial for Forth. That would
be
> a lie; Factor is almost certainly easier to optimise than Forth,
and
> for good reason; it's designed to be. There are many decisions that
> make it so.
>
> I do agree at last that Factor clearly has a parser, and yet has
the
> same basic syntax as Forth. Thus, you could consider Forth as
having a
> parser if you wanted to; but the idea lacks definition.
>
> > Slava
>
> -Billy
icpdesign — 2005-09-21 16:36:53
#Joy is not a new implementation of Joy, it is just a name to
state the inclusion of #n construction into Joy; For now, i am
just experimenting with this. My objective is to reduce any Joy
program to #Joy program, where #Joy includes the minimum set of
constructs (i, #, cons?, ..)
Here is few examples using #Joy
dup == [#1 #1] i
swap == [#1 #2] i
dip == [i #2] i
[] cons == [[#1]] i
rollup == [#1 #3 #2] i // X Y Z -> Z X Y
rolldown == [#2 #1 #3] i // X Y Z -> Y Z X
popd == [#1 #2 pop] i // Y Z -> Z
dupd == [#2 #2 #1] i // Y Z -> Y Y Z
swapd == [#2 #3 #1] i // X Y Z -> Y X Z
cons == ????
is # and i sufficient to compile Joy to #Joy, or do we need cons?
Taoufik Dachraoui
--- In
concatenative@yahoogroups.com, "icpdesign"
<taoufik.dachraoui@w...> wrote:
> When I read about string shuffling in previous messages I recalled
> the proposition of Brent Kerby to add lambda (\x not exactly
lambda,
> as noted by Dr Manfred, i think, in the sense that there is no
> formal parameters). I found the idea great, because we may define
> most of combinators using this \x construct.
>
> This looks very much as the string shuffling proposed by Dr
Manfred,
> but i found the \x less intrusive (no colon and no parens).
>
> Quadratic problem solution using string shuffling:
>
> quad == (a b c : b b * 4 a c * * - 2 a * /)
>
> 3 1 13 quad
>
> using \x
>
> quad = \a \b \c b b * 4 a c * * - 2 a * /
>
> 3 1 13 quad
>
> I propose the following construct:
>
> #n == replace the holded place in the quotation by the nth element
> of the stack; the nth element of the stack is zaped
>
> example:
>
> A B C [#1 #3 #1] => B [C A C]
>
> using this construct we can solve the quadratic as follows:
>
> quad == [#1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /] i
>
> 3 1 13 quad
>
>
> Thank you for the this Joy (#Joy)
>
> Taoufik Dachraoui
>
> --- In concatenative@yahoogroups.com, "William Tanksley, Jr"
> <wtanksleyjr@g...> wrote:
> > Slava Pestov <slava@j...> wrote:
> > > This is *exactly* how the Factor parser works. It reads tokens
> one at a
> > > time, and looks them up in the dictionary. If the token
> corresponds to a
> > > parsing word, it is executed immediately. The only difference
is
> that
> > > instead of calling COMPILE, or POSTPONE LITERAL it just
appends
> words
> > > and numbers as they are read to a list. So the output is not
> compiled
> > > tokens in the dictionary, but a list (with possibly nested
> sublists).
> >
> > In other words, Factor (1) has a defined dictionary format and
(2)
> > uses lists to store the contents of the dictionary. Forth
doesn't
> have
> > a defined format, and uses arrays. (You could, I assume, have
used
> the
> > names COMPILE, and POSTPONE LITERAL.)
> >
> > > > I fundamentally do not agree with the concept of building a
> > > > concatenative language that requires a parser. I
fundamentally
> do
> > > > agree with providing a parser and using it for complex
forms,
> such as
> > > > literal lists.
> >
> > > In Factor, lists are read using the [ and ] immediate words.
> >
> > Yes, I've seen a similar thing done for Forth, although of
course
> the
> > result isn't as pleasant as Factor. As much as I like Forth,
there
> are
> > many excellent things it doesn't do.
> >
> > > Similarly, all non-concatenative aspects of syntax, such as
> the : and ;
> > > words that delimit word definitions, are immediate words.
> >
> > Yup, makes sense.
> >
> > > So the difference between Factor and Forth is that in Forth,
the
> parser
> > > generates executable code directly. In Factor, code generation
is
> > > decoupled from parsing, which gives us a superior multi-pass
> compiler.
> >
> > Actually, the only difference I see is that Factor defines the
> parse
> > tree and gives the program access to it. Forth doesn't. This
gives
> a
> > Factor program the ability to do things that a Forth program
can't,
> > but doesn't give any optimization advantages. There's nothing
> > preventing Forth from being parsed into an intermediate format --
> in
> > fact, unlike Factor it could be parsed into some other format
> (perhaps
> > a dataflow format rather than a stack VM). As I'm sure you know,
> there
> > are good Forth optimisers.
> >
> > But I don't want to try to give a commercial for Forth. That
would
> be
> > a lie; Factor is almost certainly easier to optimise than Forth,
> and
> > for good reason; it's designed to be. There are many decisions
that
> > make it so.
> >
> > I do agree at last that Factor clearly has a parser, and yet has
> the
> > same basic syntax as Forth. Thus, you could consider Forth as
> having a
> > parser if you wanted to; but the idea lacks definition.
> >
> > > Slava
> >
> > -Billy
Narcoleptic Electron — 2005-09-21 18:10:17
On 9/21/05, icpdesign <
taoufik.dachraoui@...> wrote:
> I propose the following construct:
>
> #n == replace the holded place in the quotation by the nth element
> of the stack; the nth element of the stack is zaped
>
> example:
>
> A B C [#1 #3 #1] => B [C A C]
>
> using this construct we can solve the quadratic as follows:
>
> quad == [#1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /] i
>
> 3 1 13 quad
Using a de Bruijn indexing scheme like this has occurred and appealed
to me as well, but there is a problem: the stack is a moving target.
Consider your example:
quad == [#1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /] i
3 1 13 quad
-> 3 1 13 [#1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /] i
-> 3 1 13 #1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /
-> 3 1 13 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /
-> 3 13 1 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /
-> 13 1 3 #2 #2 * 4 #1 #3 * * - 2 #1 * /
-> 13 3 1 #2 * 4 #1 #3 * * - 2 #1 * /
-> 13 1 3 * 4 #1 #3 * * - 2 #1 * /
-> 13 3 4 #1 #3 * * - 2 #1 * /
-> 13 3 4 #3 * * - 2 #1 * /
-> 3 4 13 * * - 2 #1 * /
-> 3 52 * - 2 #1 * /
-> 156 - 2 #1 * /
Unless I've screwed something up here (entirely possible), this is
probably not what you have in mind.
The only way around this is to have some sort of preprocessor that is
invoked after anything happens to the stack, but I'm not sure how the
logic would work. It is supposed to start counting at the "13" (such
that #1 = 13, #2 = 1, #3 = 3), but how would the preprocessor know
that?
My understanding is still far below many others on the list, so
perhaps I need to be corrected on this.
Narcoleptic Electron — 2005-09-21 18:15:37
Sorry... I accidentally cut out part of my response. Second try...
On 9/21/05, Narcoleptic Electron <narcoleptic.electron@...> wrote:
> On 9/21/05, icpdesign <taoufik.dachraoui@...> wrote:
> > I propose the following construct:
> >
> > #n == replace the holded place in the quotation by the nth element
> > of the stack; the nth element of the stack is zaped
> >
> > example:
> >
> > A B C [#1 #3 #1] => B [C A C]
> >
> > using this construct we can solve the quadratic as follows:
> >
> > quad == [#1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /] i
> >
> > 3 1 13 quad
>
> Using a de Bruijn indexing scheme like this has occurred and appealed
> to me as well, but there is a problem: the stack is a moving target.
> Consider your example:
>
> quad == [#1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /] i
>
> 3 1 13 quad
> -> 3 1 13 [#1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /] i
> -> 3 1 13 #1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /
> -> 3 1 13 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /
> -> 3 13 1 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /
> -> 13 1 3 #2 #2 * 4 #1 #3 * * - 2 #1 * /
> -> 13 3 1 #2 * 4 #1 #3 * * - 2 #1 * /
> -> 13 1 3 * 4 #1 #3 * * - 2 #1 * /
> -> 13 3 4 #1 #3 * * - 2 #1 * /
> -> 13 3 4 #3 * * - 2 #1 * /
> -> 3 4 13 * * - 2 #1 * /
> -> 3 52 * - 2 #1 * /
> -> 156 - 2 #1 * /
>
> Unless I've screwed something up here (entirely possible), this is
> probably not what you have in mind.
>
> The only way around this is to have some sort of preprocessor that is
> invoked after anything happens to the stack
(that scans for # indices and replaces them with the values they are
supposed to represent, followed by removing the the originals from
the stack),
> but I'm not sure how the
> logic would work. It is supposed to start counting at the "13" (such
> that #1 = 13, #2 = 1, #3 = 3), but how would the preprocessor know
> that?
>
> My understanding is still far below many others on the list, so
> perhaps I need to be corrected on this.
>
icpdesign — 2005-09-21 20:25:42
First of all, the #n construct is just an experimentation, and thus there is
certainly many questions that need to be answered. But let me explain the
idea, hoppefully, in clear terms, and I will try to do this by examples.
Please note that for now, i will not give details on how things can be
implemented, or if there is an efficient implementation of the idea. I am
just trying to explore the # idea (inspired by the propositions by Brent
Kerby of lambda \x and the string shuffling by Manfred)
Also I will suppose that there a primite #> (sharp) that removes the sharp
variables and pops the corresponding elements from the stack, and that the
combinator i before running a program executes the sharp primite #> then
executes the program
For clarity supose that there is a primitive #> and that x == #> i
Examples:
1) a b swap == a b [#1 #2] x => a b [#1 #2] #> i => [b a] i => b a
2) 33 22 11 [#1 #2 #1 #3] x => 33 22 11 [#1 #2 #1 #3] #> i
=> [11 22 11 33] i
=> 11 22 11 33
Taoufik Dachraoui
--- In
concatenative@yahoogroups.com, Narcoleptic Electron <narcoleptic.electron@g...>
wrote:
> Sorry... I accidentally cut out part of my response. Second try...
>
> On 9/21/05, Narcoleptic Electron <narcoleptic.electron@g...> wrote:
> > On 9/21/05, icpdesign <taoufik.dachraoui@w...> wrote:
> > > I propose the following construct:
> > >
> > > #n == replace the holded place in the quotation by the nth element
> > > of the stack; the nth element of the stack is zaped
> > >
> > > example:
> > >
> > > A B C [#1 #3 #1] => B [C A C]
> > >
> > > using this construct we can solve the quadratic as follows:
> > >
> > > quad == [#1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /] i
> > >
> > > 3 1 13 quad
> >
> > Using a de Bruijn indexing scheme like this has occurred and appealed
> > to me as well, but there is a problem: the stack is a moving target.
> > Consider your example:
> >
> > quad == [#1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /] i
> >
> > 3 1 13 quad
> > -> 3 1 13 [#1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /] i
> > -> 3 1 13 #1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /
> > -> 3 1 13 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /
> > -> 3 13 1 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /
> > -> 13 1 3 #2 #2 * 4 #1 #3 * * - 2 #1 * /
> > -> 13 3 1 #2 * 4 #1 #3 * * - 2 #1 * /
> > -> 13 1 3 * 4 #1 #3 * * - 2 #1 * /
> > -> 13 3 4 #1 #3 * * - 2 #1 * /
> > -> 13 3 4 #3 * * - 2 #1 * /
> > -> 3 4 13 * * - 2 #1 * /
> > -> 3 52 * - 2 #1 * /
> > -> 156 - 2 #1 * /
> >
> > Unless I've screwed something up here (entirely possible), this is
> > probably not what you have in mind.
> >
> > The only way around this is to have some sort of preprocessor that is
> > invoked after anything happens to the stack
>
> (that scans for # indices and replaces them with the values they are
> supposed to represent, followed by removing the the originals from
> the stack),
>
> > but I'm not sure how the
> > logic would work. It is supposed to start counting at the "13" (such
> > that #1 = 13, #2 = 1, #3 = 3), but how would the preprocessor know
> > that?
> >
> > My understanding is still far below many others on the list, so
> > perhaps I need to be corrected on this.
> >
icpdesign — 2005-09-21 20:30:38
--- In
concatenative@yahoogroups.com, "icpdesign" <taoufik.dachraoui@w...> wrote:
> First of all, the #n construct is just an experimentation, and thus there is
> certainly many questions that need to be answered. But let me explain the
> idea, hoppefully, in clear terms, and I will try to do this by examples.
>
> Please note that for now, i will not give details on how things can be
> implemented, or if there is an efficient implementation of the idea. I am
> just trying to explore the # idea (inspired by the propositions by Brent
> Kerby of lambda \x and the string shuffling by Manfred)
>
> Also I will suppose that there a primite #> (sharp) that removes the sharp
> variables and pops the corresponding elements from the stack, and that the
> combinator i before running a program executes the sharp primite #> then
> executes the program
>
> For clarity supose that there is a primitive #> and that x == #> i
>
> Examples:
> 1) a b swap == a b [#1 #2] x => a b [#1 #2] #> i => [b a] i => b a
>
> 2) 33 22 11 [#1 #2 #1 #3] x => 33 22 11 [#1 #2 #1 #3] #> i
> => [11 22 11 33] i
> => 11 22 11 33
>
>
> Taoufik Dachraoui
>
> --- In concatenative@yahoogroups.com, Narcoleptic Electron <narcoleptic.electron@g...>
> wrote:
> > Sorry... I accidentally cut out part of my response. Second try...
> >
> > On 9/21/05, Narcoleptic Electron <narcoleptic.electron@g...> wrote:
> > > On 9/21/05, icpdesign <taoufik.dachraoui@w...> wrote:
> > > > I propose the following construct:
> > > >
> > > > #n == replace the holded place in the quotation by the nth element
> > > > of the stack; the nth element of the stack is zaped
> > > >
> > > > example:
> > > >
> > > > A B C [#1 #3 #1] => B [C A C]
> > > >
> > > > using this construct we can solve the quadratic as follows:
> > > >
> > > > quad == [#1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /] i
> > > >
> > > > 3 1 13 quad
> > >
> > > Using a de Bruijn indexing scheme like this has occurred and appealed
> > > to me as well, but there is a problem: the stack is a moving target.
> > > Consider your example:
> > >
> > > quad == [#1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /] i
> > >
> > > 3 1 13 quad
> > > -> 3 1 13 [#1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /] i
> > > -> 3 1 13 #1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /
> > > -> 3 1 13 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /
> > > -> 3 13 1 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /
> > > -> 13 1 3 #2 #2 * 4 #1 #3 * * - 2 #1 * /
> > > -> 13 3 1 #2 * 4 #1 #3 * * - 2 #1 * /
> > > -> 13 1 3 * 4 #1 #3 * * - 2 #1 * /
> > > -> 13 3 4 #1 #3 * * - 2 #1 * /
> > > -> 13 3 4 #3 * * - 2 #1 * /
> > > -> 3 4 13 * * - 2 #1 * /
> > > -> 3 52 * - 2 #1 * /
> > > -> 156 - 2 #1 * /
> > >
> > > Unless I've screwed something up here (entirely possible), this is
> > > probably not what you have in mind.
> > >
> > > The only way around this is to have some sort of preprocessor that is
> > > invoked after anything happens to the stack
> >
> > (that scans for # indices and replaces them with the values they are
> > supposed to represent, followed by removing the the originals from
> > the stack),
> >
> > > but I'm not sure how the
> > > logic would work. It is supposed to start counting at the "13" (such
> > > that #1 = 13, #2 = 1, #3 = 3), but how would the preprocessor know
> > > that?
> > >
> > > My understanding is still far below many others on the list, so
> > > perhaps I need to be corrected on this.
> > >
icpdesign — 2005-09-21 21:48:25
I would like here to give an example to show how a string shuffle construct
can be rewritten using #> primitive
string shuffle:
a b C ( a b [c | T] : a dup b * c * * T dup)
-----------------////////////////////
using #>:
a b C [#3 dup #2 * #1 uncons [#2 * * #1 dup] #> i] #> i
=> a b C [a dup b * C uncons [#2 * * #1 dup] #> i] i
=> a dup b * C uncons [#2 * * #1 dup] #> i
=> a dup b * c T [#2 * * #1 dup] #> i
=> a dup b * [c * * T dup] i
=> a dup b * c * * T dup
----////////////////////
Note that #> do not replace the # variables in the sub-quotations; only
the # variables in the first level are replaced.
As you can see the string shuffle construct is more compact, but #> can be
thought of as a lower level primitive. the question is can we transform any
string shuffle construct to a program using #>.
Taoufik Dachraoui
--- In concatenative@yahoogroups.com, "icpdesign" <taoufik.dachraoui@w...> wrote:
> --- In concatenative@yahoogroups.com, "icpdesign" <taoufik.dachraoui@w...> wrote:
> > First of all, the #n construct is just an experimentation, and thus there is
> > certainly many questions that need to be answered. But let me explain the
> > idea, hoppefully, in clear terms, and I will try to do this by examples.
> >
> > Please note that for now, i will not give details on how things can be
> > implemented, or if there is an efficient implementation of the idea. I am
> > just trying to explore the # idea (inspired by the propositions by Brent
> > Kerby of lambda \x and the string shuffling by Manfred)
> >
> > Also I will suppose that there a primite #> (sharp) that removes the sharp
> > variables and pops the corresponding elements from the stack, and that the
> > combinator i before running a program executes the sharp primite #> then
> > executes the program
> >
> > For clarity supose that there is a primitive #> and that x == #> i
> >
> > Examples:
> > 1) a b swap == a b [#1 #2] x => a b [#1 #2] #> i => [b a] i => b a
> >
> > 2) 33 22 11 [#1 #2 #1 #3] x => 33 22 11 [#1 #2 #1 #3] #> i
> > => [11 22 11 33] i
> > => 11 22 11 33
> >
> >
> > Taoufik Dachraoui
> >
> > --- In concatenative@yahoogroups.com, Narcoleptic Electron
<narcoleptic.electron@g...>
> > wrote:
> > > Sorry... I accidentally cut out part of my response. Second try...
> > >
> > > On 9/21/05, Narcoleptic Electron <narcoleptic.electron@g...> wrote:
> > > > On 9/21/05, icpdesign <taoufik.dachraoui@w...> wrote:
> > > > > I propose the following construct:
> > > > >
> > > > > #n == replace the holded place in the quotation by the nth element
> > > > > of the stack; the nth element of the stack is zaped
> > > > >
> > > > > example:
> > > > >
> > > > > A B C [#1 #3 #1] => B [C A C]
> > > > >
> > > > > using this construct we can solve the quadratic as follows:
> > > > >
> > > > > quad == [#1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /] i
> > > > >
> > > > > 3 1 13 quad
> > > >
> > > > Using a de Bruijn indexing scheme like this has occurred and appealed
> > > > to me as well, but there is a problem: the stack is a moving target.
> > > > Consider your example:
> > > >
> > > > quad == [#1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /] i
> > > >
> > > > 3 1 13 quad
> > > > -> 3 1 13 [#1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /] i
> > > > -> 3 1 13 #1 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /
> > > > -> 3 1 13 #2 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /
> > > > -> 3 13 1 #3 #2 #2 * 4 #1 #3 * * - 2 #1 * /
> > > > -> 13 1 3 #2 #2 * 4 #1 #3 * * - 2 #1 * /
> > > > -> 13 3 1 #2 * 4 #1 #3 * * - 2 #1 * /
> > > > -> 13 1 3 * 4 #1 #3 * * - 2 #1 * /
> > > > -> 13 3 4 #1 #3 * * - 2 #1 * /
> > > > -> 13 3 4 #3 * * - 2 #1 * /
> > > > -> 3 4 13 * * - 2 #1 * /
> > > > -> 3 52 * - 2 #1 * /
> > > > -> 156 - 2 #1 * /
> > > >
> > > > Unless I've screwed something up here (entirely possible), this is
> > > > probably not what you have in mind.
> > > >
> > > > The only way around this is to have some sort of preprocessor that is
> > > > invoked after anything happens to the stack
> > >
> > > (that scans for # indices and replaces them with the values they are
> > > supposed to represent, followed by removing the the originals from
> > > the stack),
> > >
> > > > but I'm not sure how the
> > > > logic would work. It is supposed to start counting at the "13" (such
> > > > that #1 = 13, #2 = 1, #3 = 3), but how would the preprocessor know
> > > > that?
> > > >
> > > > My understanding is still far below many others on the list, so
> > > > perhaps I need to be corrected on this.
> > > >
William Tanksley, Jr — 2005-09-22 04:43:12
icpdesign <
taoufik.dachraoui@...> wrote:
> I would like here to give an example to show how a string shuffle construct
> can be rewritten using #> primitive
> string shuffle:
> a b C ( a b [c | T] : a dup b * c * * T dup)
This is a lambda operator -- is there some reason you're calling it a
string shuffle?
> -----------------////////////////////
> using #>:
> a b C [#3 dup #2 * #1 uncons [#2 * * #1 dup] #> i] #> i
This also looks like a lambda operator to me, but without lexical scoping.
> As you can see the string shuffle construct is more compact, but #> can be
> thought of as a lower level primitive. the question is can we transform any
> string shuffle construct to a program using #>.
Definitely. The process is called beta-reduction; it's the inverse of
lambda-expansion (the two together form lambda calculus).
I'm not particularly interested in either; they're antithetical to
pure concatenative languages in much the same way that modifiable
variables are antithetical to pure functional applicative languages.
> Taoufik Dachraoui
-Billy
icpdesign — 2005-09-22 12:18:32
--- In
concatenative@yahoogroups.com, "William Tanksley, Jr"
<wtanksleyjr@g...> wrote:
> icpdesign <taoufik.dachraoui@w...> wrote:
> > I would like here to give an example to show how a string
shuffle construct
> > can be rewritten using #> primitive
> > string shuffle:
> > a b C ( a b [c | T] : a dup b * c * * T dup)
>
> This is a lambda operator -- is there some reason you're calling
it a
> string shuffle?
Sorry, read as shuffle string. This is the generalization,
proposed by Manfred, of the shuffle string (eg. "ab--ba").
I went back to the messages archive and I found that there
is a shuffle operator in cK (eg. "ab--ba" shuffle ), also I
saw another syntax using integers like in [0 1] shuffle
see messages 1698, 1696, 1736, 1778
>
> > -----------------////////////////////
> > using #>:
> > a b C [#3 dup #2 * #1 uncons [#2 * * #1 dup] #> i] #> i
>
> This also looks like a lambda operator to me, but without lexical
scoping.
>
> > As you can see the string shuffle construct is more compact, but
#> can be
> > thought of as a lower level primitive. the question is can we
transform any
> > string shuffle construct to a program using #>.
>
> Definitely. The process is called beta-reduction; it's the inverse
of
> lambda-expansion (the two together form lambda calculus).
>
> I'm not particularly interested in either; they're antithetical to
> pure concatenative languages in much the same way that modifiable
> variables are antithetical to pure functional applicative
languages.
>
I do not see #> nor #n as lambda operators, I am trying to write
something about this. #> is a primitive that takes a quotation or
a # variable and rewrite them without # variables
A primitive is a way to escape the world and do some magic stuff, the
only constrained is that they must follow strict rules: the only
side-effect is to modify the stack before returning
Ex.
5 [#1] #> ==> [5]
4 5 #2 #> ==> 5 4
Note: #1 and #2 belongs to the new type (sharp integers)
I hope I will be able to expand this idea more precisely in my next
messages. For now I am just exploring and having fun with Joy.
> > Taoufik Dachraoui
>
> -Billy
- Taoufik Dachraoui
icpdesign — 2005-09-22 13:46:44
--- In
concatenative@yahoogroups.com, "William Tanksley, Jr"
<wtanksleyjr@g...> wrote:
> icpdesign <taoufik.dachraoui@w...> wrote:
> > I would like here to give an example to show how a string
shuffle construct
> > can be rewritten using #> primitive
> > string shuffle:
> > a b C ( a b [c | T] : a dup b * c * * T dup)
>
> This is a lambda operator -- is there some reason you're calling
it a
> string shuffle?
>
> > -----------------////////////////////
> > using #>:
> > a b C [#3 dup #2 * #1 uncons [#2 * * #1 dup] #> i] #> i
>
> This also looks like a lambda operator to me, but without lexical
scoping.
>
> > As you can see the string shuffle construct is more compact, but
#> can be
> > thought of as a lower level primitive. the question is can we
transform any
> > string shuffle construct to a program using #>.
>
> Definitely. The process is called beta-reduction; it's the inverse
of
a b C [#3 dup #2 * #1 uncons [#2 * * #1 dup] #> i] #>
==> [a dup b * C uncons [#2 * * #1 dup] #> i]
the primitive #> is just reading the stack, popping elements from
the stack (a b C) then it returns a valid Joy program like most of
the existing primitives (eg. dup, swap, cons, ...)
> lambda-expansion (the two together form lambda calculus).
>
> I'm not particularly interested in either; they're antithetical to
> pure concatenative languages in much the same way that modifiable
> variables are antithetical to pure functional applicative
languages.
>
> > Taoufik Dachraoui
>
> -Billy
William Tanksley, Jr — 2005-09-24 16:59:36
icpdesign <
taoufik.dachraoui@...> wrote:
>"William Tanksley, Jr" <wtanksleyjr@g...> wrote:
>>>thought of as a lower level primitive. the question is can we
>>>transform any
>>>string shuffle construct to a program using #>.
>>Definitely. The process is called beta-reduction; it's the inverse
>>of lambda-expansion (the two together form lambda calculus).
> a b C [#3 dup #2 * #1 uncons [#2 * * #1 dup] #> i] #>
> ==> [a dup b * C uncons [#2 * * #1 dup] #> i]
> the primitive #> is just reading the stack, popping elements from
> the stack (a b C) then it returns a valid Joy program like most of
> the existing primitives (eg. dup, swap, cons, ...)
That doesn't demonstrate that it's not a lambda operator. It actually is.
It differs from the lisp lambda operator in only two ways: first, it
doesn't provide scoping; and second, it doesn't allow you to name your
lambda parameters. Both serve to make it a little simpler, but neither
reduces its power.
So any proof about lambda will also apply to your operator.
-Billy
icpdesign — 2005-09-24 18:13:02
--- In
concatenative@yahoogroups.com, "William Tanksley, Jr" <wtanksleyjr@g...> wrote:
> icpdesign <taoufik.dachraoui@w...> wrote:
> >"William Tanksley, Jr" <wtanksleyjr@g...> wrote:
> >>>thought of as a lower level primitive. the question is can we
> >>>transform any
> >>>string shuffle construct to a program using #>.
>
> >>Definitely. The process is called beta-reduction; it's the inverse
> >>of lambda-expansion (the two together form lambda calculus).
>
> > a b C [#3 dup #2 * #1 uncons [#2 * * #1 dup] #> i] #>
> > ==> [a dup b * C uncons [#2 * * #1 dup] #> i]
>
> > the primitive #> is just reading the stack, popping elements from
> > the stack (a b C) then it returns a valid Joy program like most of
> > the existing primitives (eg. dup, swap, cons, ...)
>
> That doesn't demonstrate that it's not a lambda operator. It actually is.
>
Let
dig == [] swap 1 - [cons] times dip; # pop nth element and push on top
peek == [] swap 1 - [cons] times swap dup [swap] dip swap dip; # read nth
element and push on top
and #> (sdig) <# (speek) are defined as
sdig == [issharp] [uncons swap pop 0 strtol dig] [] ifte;
speek == [issharp] [uncons swap pop 0 strtol peek] [] ifte;
Note the word issharp checks if the element on top of stack
is a sharp integer; since Joy does not have sharp integer type, I defined
strings "#n" to represent sharp integers. see message 2704 ( Defining
sharp primitives (#> and <#) using Joy) for more details on how to
implement the sharp primitives in Joy.
I cannot say that sdig (#>) and speek (<#) are lambda operators.
> It differs from the lisp lambda operator in only two ways: first, it
> doesn't provide scoping; and second, it doesn't allow you to name your
> lambda parameters. Both serve to make it a little simpler, but neither
> reduces its power.
>
> So any proof about lambda will also apply to your operator.
>
> -Billy
- Taoufik Dachraoui
William Tanksley, Jr — 2005-09-25 05:07:41
icpdesign <
taoufik.dachraoui@...> wrote:
> "William Tanksley, Jr" <wtanksleyjr@g...> wrote:
>>That doesn't demonstrate that it's not a lambda operator. It actually is.
> The following shows that #> (sdig) and #< (speek) are defined using Joy,
> Does this prove that this 2 primitives are not lambda?
No; if you implemented them in machine language they'd still be lambda
operators. The question is what syntax rules they create, not what
language they're implemented in.
Why do you not want them to be lambda?
-Billy
icpdesign — 2005-09-25 10:28:36
--- In
concatenative@yahoogroups.com, "William Tanksley, Jr" <wtanksleyjr@g...> wrote:
> icpdesign <taoufik.dachraoui@w...> wrote:
> > "William Tanksley, Jr" <wtanksleyjr@g...> wrote:
> >>That doesn't demonstrate that it's not a lambda operator. It actually is.
>
> > The following shows that #> (sdig) and #< (speek) are defined using Joy,
> > Does this prove that this 2 primitives are not lambda?
>
> No; if you implemented them in machine language they'd still be lambda
> operators. The question is what syntax rules they create, not what
> language they're implemented in.
>
> Why do you not want them to be lambda?
I just wanted to know if sdig and speek useful as Joy primitives?
If not why?
sdig == [issharp] [sharp2int dig] [] ifte
speek == [issharp] [sharp2int peek] [] ifte
where
dig == [] swap 1 - [cons] times dip;
peek == [] swap 1 - [cons] times swap dup [swap] dip swap dip;
issharp : returns true if the top element is a string of the form "#n"
sharp2int : converts a string "#n" to integer n
note that dig is a generalization of dig2, dig3 ... defined in the paper
written by Brent Kerby, the same for peek.
example: 3 dig == dig3
Examples on how sdig is used:
10 20 30 ["#3" 1 2 "#2" ] [sdig] map
==> 10 20 30 [10 1 2 20]
Question about lambda abstractions:
if sdig is a lambda abstraction then all Joy operators are lambda abstractions
example: (\ S : S dup) is a lambda abstraction of dup, where S is the stack
and the result is a modified stack S (S')
The same for sdig: (\ S : S sdig) JoyStack => JoyStack'
if this is what you meant we agree.
>
> -Billy
Taoufik Dachraoui
William Tanksley, Jr — 2005-09-25 17:32:33
icpdesign <
taoufik.dachraoui@...> wrote:
> I just wanted to know if sdig and speek useful as Joy primitives?
> If not why?
I would say no, but I'm being dogmatic. They are anti-concatenative
(that is, they destroy the concatenative properties of the language to
the extent that they're used). And although they're as powerful as
lambda expressions (which are also anti-concatenative), they're harder
to use: they don't allow you to name your lambda variables.
There are also benefits to using them, of course, and it was
definitely clever of you to have come up with them. But *I* definitely
wouldn't use them, and when teaching Joy I wouldn't teach people how
to use them -- because I see Joy as the prototypical concatenative
language, and sdig etc are not concatenative.
> Question about lambda abstractions:
> if sdig is a lambda abstraction then all Joy operators are lambda abstractions
> example: (\ S : S dup) is a lambda abstraction of dup, where S is the stack
> and the result is a modified stack S (S')
> The same for sdig: (\ S : S sdig) JoyStack => JoyStack'
> if this is what you meant we agree.
No, sdig and speek are lambda operators because they take as input a
program description including zero or more free variables and a set of
values, and produce as output a program description with the free
variables bound to the given values, according to the rules of lambda
substitution. See
http://en.wikipedia.org/wiki/Lambda_expression
> Taoufik Dachraoui
-Billy