# 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 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.

    --

    Some people open all the Windows;       John Cowan

    wise wives welcome the spring           jcowan@...

    by moving the Unix.                     http://www.reutershealth.com

      --ad for Unix Book Units (U.K.)       http://www.ccil.org/~cowan

            (see http://cm.bell-labs.com/cm/cs/who/dmr/unix3image.gif)

## 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

    John Cowan scripsit:

     > 		--http://www.ccil.org/upc, verse 41

     Oops.  That's http://www.ccil.org/~cowan/upc , verse 41.

    --

    "As we all know, civil libertarians are not     John Cowan

    the friskiest group around -- comes from        jcowan@...

    forever being on the qui vive for the sound     http://www.ccil.org/~cowan

    of jack-booted fascism coming down the pike."           --Molly Ivins

## 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

    http://www.malevole.com/mv/misc/killerquiz/

## 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