Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.forth > #135613

Re: Postscript in 4tH

From Kragen Javier Sitaker <kragen@canonical.org>
Newsgroups comp.lang.forth, comp.lang.postscript, alt.folklore.computers, comp.lang.lisp
Subject Re: Postscript in 4tH
Date 2026-09-08 22:38 -0300
Organization Primarily biological and memetic
Message-ID <87o6e7cifh.fsf@debian> (permalink)
References <117hl2n$1ne4b$1@dont-email.me> <871pb7l36f.fsf@nightsong.com> <nnd$47db062e$781955bb@280ad1e4adf8c230> <875x0gib1k.fsf@debian> <117prfv$gq7e$3@dont-email.me>

Cross-posted to 4 groups.

Show all headers | View raw


(Please forgive in advance the LLM-like wording of what follows.  I
wrote it entirely by hand, including reading the documents I linked, but
I’ve been coaxing 

Lawrence D’Oliveiro <ldo@nz.invalid> writes:
> On Tue, 08 Sep 2026 02:09:43 -0300, Kragen Javier Sitaker wrote:
>> I think of PostScript as being Lisp, more or less, but with
>> quasi-Forth syntax ...
>
> PostScript is homoiconic, but the resemblance to Lisp ends there. Lisp
> has macros, PostScript doesn’t. Lisp needs macros,

At the time that PostScript was designed, most Lisps did not have
macros; they still had fexprs.  Some Lisps did have macros, most notably
MACLISP, but they had not yet become central to language style the way
they are today.  Remember that PostScript first shipped in 01982, when
the world was dominated by BASIC, FORTRAN, and COBOL.

PostScript does in fact have something like readmacros; in particular,
readhexstring was the recommended way to handle image data, so that the
image data didn’t have to be loaded into a PostScript object by way of
the PostScript parser.  But, for the most part, like Smalltalk,
PostScript uses a lightweight lambda syntax for most of the things you
would use macros for in Lisp.

PostScript has several other key similarities to Lisp.  Referencing Paul
Graham’s “What Made Lisp Different” <https://paulgraham.com/diff.html>:

1. Conditionals.  Yes, but all languages had conditionals by 01982.

2. A function type.  “In Lisp, functions are first class objects--
they’re a data type just like integers, strings, etc, and have a literal
representation, can be stored in variables, can be passed as arguments,
and so on.”  Yes; PostScript goes even harder here than Lisp, because
there’s actually no way to define a named function in PostScript.  You
have to define an anonymous function and pass it as an argument to /bind
in order to give it a name.  This is somewhat related to homoiconicity
but is not the same thing.

3. Recursion.  Yes, but only FORTRAN lacked recursion by then.  COBOL
and BASIC lacked local variables, though, which generally made recursion
impractical.

4. A new concept of variables. “In Lisp, all variables are effectively
pointers. Values are what have types, not variables, and assigning or
binding variables means copying pointers, not what they point to.”

PostScript does work this way, as does Smalltalk (not coincidentally,
from the same lab, which was also heavily into Lisp). It’s easy to
forget how unusual this was, because now languages more or less like
this dominate the scene: Python, JS, Ruby, PHP, Lua, and to some extent,
even Java and C#.  But none of the other popular languages of the day
worked that way.  Not conventional assembly (untyped, and variables are
memory addresses), not BASIC, not C, not Pascal, not awk, not the Bourne
shell, not the C shell, not Forth, not BLISS, not FOCAL, not Mesa, not
ML, not Algol, not PL/I, not Ada.

Moreover, in both Lisp and in PostScript, this extends to not just
regular variables, but any item of any data structure.  In BASIC, C,
FORTRAN, or Pascal, you might have arrays of strings, or arrays of
integers, or in some cases arrays of 3-element arrays of integers, but
you couldn’t have an array of arbitrary-type values.  PostScript arrays
contain arbitrary-type values, as do the most common type of vectors in
Common Lisp, and lists in any Lisp.

5. Garbage-collection.  Yes.

6. Programs composed of expressions.  Not really, but closer than any of
the currently popular languages.

7. A symbol type.  Yes, PostScript distinguishes /names from strings,
which is arguably the feature of all of these that is most distinctive
to Lisp (the only other languages that do this are Ruby, Smalltalk, and,
in a way, JS), and PostScript shares it.

8. A notation for code using trees of symbols.  Yes.  The only
difference is that in conventional Lisp they’re binary trees, while in
PostScript they’re N-ary ordered trees.

9. “The whole language always available”, i.e., arbitrarily overlapping
compile time and runtime.  Yes.

Given that PostScript clearly hits 8 out of 9 of these criteria, and
arguably all 9, while no then-popular language other than Lisp came
anywhere close, I think it’s pretty clear that it mostly belongs to the
Lisp family.  Its resemblance to Lisp is much deeper than just being
homoiconic.

Forth hits #1, #3, and #9.

****

If you’re not familiar with PostScript, you might be puzzled by #8.
Let’s define a new function that tells us the sign of a number and bind
it to the symbol `sign`:

    GS>/sign {dup 0 gt {pop /positive} {0 lt {/negative} {/zero} ifelse} ifelse} def
    GS>3 sign =  0 sign =  -53 sign =
    positive
    zero
    negative

We can use `load` on the quoted symbol to find the current definition of
`sign` without executing it:

    GS>/sign load ===
    {dup 0 gt {pop /positive} {0 lt {/negative} {/zero} ifelse} ifelse}

It turns out that’s an array of length 3:

    GS>/sign load type =
    arraytype
    GS>/sign load length =
    6

We can fetch things from it:

    GS>/sign load 0 get ===  /sign load 3 get ===
    dup
    {pop /positive}

See, the zeroth thing in the array is `dup`.  Which is a “name”, not a
string, which PostScript also has:

    GS>/sign load 0 get type =  (hello, world) type =
    nametype
    stringtype

We can even modify the array in place to modify the code, although we
can’t lengthen it:

    GS>/sign load 0 /pop cvx put
    GS>/sign load ===
    {pop 0 gt {pop /positive} {0 lt {/negative} {/zero} ifelse} ifelse}

Now it crashes because `pop` drops the operand `gt` wants to compare:

    GS>3 sign =
    Error: /stackunderflow in --gt--

You can define a regular, non-executable array and turn it into a
function with regular programming operators:

    GS>/square 2 array def
    GS>square ===
    [null null]
    GS>square 0 /dup cvx put  square ===
    [dup null]
    GS>square 1 /mul cvx put  square ===
    [dup mul]
    GS>/square square cvx def  3 square ===
    9
    GS>/square load ===
    {dup mul}

If you’re familiar with old Lisp systems, almost all of this will seem
very familiar, but the syntax will seem backwards, and you’ll wonder why
code is stored in arrays instead of lists.

You can also define a function that uses the PostScript reader `token`
to consume input, similar to `word` in Forth but much more similar to
`read` in Lisp:

    GS>/typeof {currentfile token pop type} def
    GS>typeof dup ===
    nametype
    GS>typeof 34 ===
    integertype
    GS>typeof (hello, world) ===
    stringtype

Where `token` diverges radically from Forth is that it will read an
entire nested expression (without executing it), not just a token as the
term is normally understood:

    GS>typeof {dup mul dup add} ===
    arraytype

(Note that this *doesn't* work for [3 4], because `[` and `]` are
operators, so [3 4] isn’t an expression!)

Given that you can parse expressions from the input stream at compile
time and generate code at runtime, you can obviously implement Lisp-like
macros in PostScript.  It just isn’t an established practice.

The one very non-Lispy weird thing, which doesn't have a similar feature
in any other language I know of, is that the executable bit set by `cvx`
is an attribute of references, not objects.  So `{dup mul}` and `[dup
mul]` can be the same object as seen through two different references.
That’s why making `square` executable required calling `def` again
rather than just `cvx`.

****

To get a feel for what 01982 was like, check out the October 01982 issue
of the CACM <https://dl.acm.org/toc/cacm/1982/25/10> or of Byte
<https://archive.org/details/BYTE_Vol_07-10_1982-10_Computers_in_Business>.

The languages mentioned in the CACM issue are Fortran (in the table of
contents); COBOL, PL/I, and APL (in the data administration article);
Algol 60 and Macsyma but not Lisp (in the “microanalysis” (profiling)
paper, which also included some Algol as pseudocode); Lisp as the
pseudocode, though without naming it (in Kornfield’s paper about
“combinatorially implosive” algorithms); none (in the geometric
algorithm paper); Fortran (in the gamma-deviate-generation comment); and
Ada (in the “ACM Forum” letters column.)  It’s a total head trip.

In Byte, the languages mentioned are 68000 assembler, FORTRAN 77,
Pascal, BASIC, COBOL, and C (in the Cromemco ad); Visicalc, BASIC,
Pascal, FORTH, and BASIC again (in the table of contents); Pascal
(though it’s really talking about the UCSD Pascal OS) and FORTRAN (in
the editorial); BASIC, COBOL, BASIC again, and LISP (in the Letters);
Visicalc and something called Microfinesse (in the Visicalc article);
etc.

That’s just the first 41 pages; I’ll leave the other 490 pages to you.

Kragen

Back to comp.lang.forth | Previous | Next — Previous in thread | Next in thread | Find similar | Unroll thread


Thread

Postscript in 4tH Hans Bezemer <the.beez.speaks@gmail.com> - 2026-09-05 19:55 +0200
  Re: Postscript in 4tH Paul Rubin <no.email@nospam.invalid> - 2026-09-05 15:54 -0700
    Re: Postscript in 4tH albert@spenarnc.xs4all.nl - 2026-09-06 11:08 +0200
      Re: Postscript in 4tH Paul Rubin <no.email@nospam.invalid> - 2026-09-07 09:24 -0700
        Re: Postscript in 4tH albert@spenarnc.xs4all.nl - 2026-09-07 23:47 +0200
      Re: Postscript in 4tH Kragen Javier Sitaker <kragen@canonical.org> - 2026-09-08 02:09 -0300
        Re: Postscript in 4tH Lawrence D’Oliveiro <ldo@nz.invalid> - 2026-09-08 20:34 +0000
          Re: Postscript in 4tH Kragen Javier Sitaker <kragen@canonical.org> - 2026-09-08 22:38 -0300
            Re: Postscript in 4tH Lawrence D’Oliveiro <ldo@nz.invalid> - 2026-09-09 03:05 +0000
              XML in Pythong (was: Re: Postscript in 4tH) Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> - 2026-09-09 15:00 +0800
              Re: Postscript in 4tH Kragen Javier Sitaker <kragen@canonical.org> - 2026-09-10 01:06 -0300
                Re: Postscript in 4tH Lawrence D’Oliveiro <ldo@nz.invalid> - 2026-09-10 07:38 +0000
                Re: Postscript in 4tH Peter Flass <Peter@Iron-Spring.com> - 2026-09-10 07:39 -0700
                Re: Postscript in 4tH antispam@fricas.org (Waldek Hebisch) - 2026-09-15 16:04 +0000
                Re: Postscript in 4tH Paul Rubin <no.email@nospam.invalid> - 2026-09-15 09:24 -0700
            Re: Postscript in 4tH Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> - 2026-09-09 15:15 +0800
              Re: Postscript in 4tH Kragen Javier Sitaker <kragen@canonical.org> - 2026-09-10 01:11 -0300
            Re: Postscript in 4tH anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2026-09-09 07:35 +0000
              Re: Postscript in 4tH Lawrence D’Oliveiro <ldo@nz.invalid> - 2026-09-09 08:10 +0000
                Re: Postscript in 4tH Hans Bezemer <the.beez.speaks@gmail.com> - 2026-09-10 17:58 +0200
              Re: Postscript in 4tH Kragen Javier Sitaker <kragen@canonical.org> - 2026-09-10 01:44 -0300
            Re: Postscript in 4tH Paul Rubin <no.email@nospam.invalid> - 2026-09-09 03:01 -0700
            Re: Postscript in 4tH albert@spenarnc.xs4all.nl - 2026-09-09 14:05 +0200
          Re: Postscript in 4tH anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2026-09-09 06:42 +0000
            Re: Postscript in 4tH Lawrence D’Oliveiro <ldo@nz.invalid> - 2026-09-09 08:28 +0000
              Re: Postscript in 4tH anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2026-09-09 10:03 +0000
                Re: Postscript in 4tH Lawrence D’Oliveiro <ldo@nz.invalid> - 2026-09-09 22:17 +0000
              Re: Postscript in 4tH albert@spenarnc.xs4all.nl - 2026-09-09 14:42 +0200
                Re: Postscript in 4tH antispam@fricas.org (Waldek Hebisch) - 2026-09-09 19:17 +0000
                Re: Postscript in 4tH albert@spenarnc.xs4all.nl - 2026-09-10 12:22 +0200
                Re: Postscript in 4tH Lawrence D’Oliveiro <ldo@nz.invalid> - 2026-09-09 21:54 +0000
                Re: Postscript in 4tH Paul Rubin <no.email@nospam.invalid> - 2026-09-09 22:14 -0700
                Re: Postscript in 4tH albert@spenarnc.xs4all.nl - 2026-09-10 12:30 +0200
            Re: Postscript in 4tH albert@spenarnc.xs4all.nl - 2026-09-09 14:36 +0200
    Re: Postscript in 4tH Hans Bezemer <the.beez.speaks@gmail.com> - 2026-09-06 14:27 +0200
      Re: Postscript in 4tH Hans Bezemer <the.beez.speaks@gmail.com> - 2026-09-08 17:18 +0200

csiph-web