Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.forth > #135638
| 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-10 01:06 -0300 |
| Organization | Primarily biological and memetic |
| Message-ID | <87h5jxagx6.fsf@debian> (permalink) |
| References | (2 earlier) <nnd$47db062e$781955bb@280ad1e4adf8c230> <875x0gib1k.fsf@debian> <117prfv$gq7e$3@dont-email.me> <87o6e7cifh.fsf@debian> <117qido$ng8h$3@dont-email.me> |
Cross-posted to 4 groups.
(Apologies if this sounds like AI slop; I’ve been interacting with
Claude Opus 5 a lot today, and although I didn’t use any LLM to write
any of this, its voice may be leaking into mine.)
Lawrence D’Oliveiro <ldo@nz.invalid> writes:
> On Tue, 08 Sep 2026 22:38:58 -0300, Kragen Javier Sitaker wrote:
>> 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.
>
> It’s just a function. It’s like saying Python has “readmacros” because
> the JSON and XML library modules have functions for reading input
It’s a function that runs while the PostScript source code is being
read, which means that it can change the interpretation of that source
code. That’s a way that PostScript is like Lisp and unlike Python.
Python’s JSON and XML library modules’ functions are different from Lisp
readmacros because they cannot read from the Python input stream; the
Python parser finishes running before any Python code runs. There’s no
way to do the equivalent of this readmacro-like example from my post in
Python:
GS>/typeof {currentfile token pop type} def
GS>typeof dup ===
nametype
To sharpen this, note that it doesn’t matter that `dup` is defined:
GS>typeof oagjijgw ===
nametype
You’d have to be able to write something in Python which gave you a
session like this:
$ python3
Python 3.11.2 (main, Aug 26 2024, 07:20:54) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from somemodule import typeof
>>> typeof(oagjijgw) # or ideally even without the parentheses
<ast.Name object at 0x7f52bb870130>
I claim that this `typeof` function is impossible to write in Python,
because the parser throws a NameError before `typeof` has a chance to
run, given that I haven’t defined `oagjijgw` previously.
That is how PostScript’s `token` (and `readstring`, `readhexstring`,
etc.) are more like Lisp readmacros than they are like any function in
any possible Python module.
>> 2. A function type. “In Lisp, functions are first class objects-- [...]
>
> Homoiconicity comes in because the function body is stored in an array
> object, which is a PostScript language type, and its contents are also
> objects of various PostScript language types.
The analogous statements were true of most Lisps in 01982, because
`lambda` was basically implemented as `quote` when it was the argument
of a function (or subr), but they are true of almost no other
programming languages except assembly language, so this is another
similarity between PostScript and Lisp.
However, the analogous statements are not true of most current Lisps,
which have adopted the position that lambda-expressions are lists, and
functions are not lists. SBCL, for example:
* (type-of 3)
(INTEGER 0 4611686018427387903)
* (type-of (lambda () 3))
COMPILED-FUNCTION
* (type-of '(lambda () 3))
CONS
Or Racket:
> (pair? (lambda () 3))
#f
> (pair? '(lambda () 3))
#t
> (procedure? (lambda () 3))
#t
> (procedure? '(lambda () 3))
#f
Perhaps you would argue that PostScript is homoiconic and SBCL and
Racket are not. However, most people who know the word do consider them
to be homoiconic, because, even though the *runtime representation* of
functions does not contain objects of various Lisp language types, the
*source code* is just a Lisp list, so building Lisp code in Lisp is
easy:
> (eval (cons 'lambda (cons '() (cons 3 '()))))
#<procedure>
> ((eval (cons 'lambda (cons '() (cons 3 '())))))
3
Unfortunately, this definition of homoiconicity suggests that any
language compiled from sequences of characters is “homoiconic” unless
for some reason it lacks the ability to manipulate sequences of
characters. So “homoiconic languages” is no longer a logically
well-defined category; it’s more a sliding scale of what programmers
find easy or difficult.
But, look again at PG’s explanation of what he means by “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.”
The only one of these that plausibly relates to homoiconicity is “have a
literal representation”. In old Lisps you could just print out the
definition of any function as an S-expression, but, as we see above with
Racket, that is not currently the case; we get something like
`#<FUNCTION (LAMBDA ()) {534960EB}>` or `#<procedure>`. But that’s
still a string representation! And *most* languages don't have the
ability to print out, for example, struct literals.
What makes a “string literal” or “integer literal” a *literal* is that
you can put it in your source code without having to give it a name.
C since C99 has “compound literals” for structs and arrays
<https://en.cppreference.com/c/language/compound_literal>, which look
like `(struct foo){3, "hi"}`, and evaluate to a value of that type
without having to bind it to a name. Older versions of ANSI C treated
structs the way C still treats functions; instead of writing
return (struct point){x + dx, y + dy};
you had to write
struct point p = {x + dx, y + dy};
return p;
Golang struct literals are the same thing
<https://go.dev/tour/moretypes/5>: “A struct literal denotes a newly
allocated struct value by listing the values of its fields.”
JS has “array literals”
<https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Array#array_literal_notation>
for which the example given is `["Apple", "Banana"]`.
So the most plausible gloss of PG’s “has a literal representation” is
that there are Lisp expressions which create new functions when
evaluated, which is just as true of modern Common Lisp and Scheme as it
was of Franz Lisp in 01982. And it’s obviously true of PostScript, but
not of C, Pascal, BASIC, etc.
> Where PostScript is lacking is in missing support for lexical binding.
> Anybody who has done much PostScript programming knows how awkward it
> is to simulate anything resembling local variables.
This is another similarity between PostScript and the then-popular
Lisps, which almost entirely used dynamic scope like PostScript, rather
than lexical scope like almost all other programming languages.
However, it is true that the facility is more convenient to use in Lisp.
Here’s a slightly reformatted interactive session showing how awkward it
is to simulate anything resembling local variables, in case anyone is
curious:
GS>/! {exch def} def
GS>/printbuf ( ) def /. {printbuf cvs print} def
GS>/hanoi {
4 dict begin
/n ! /dest ! /storage ! /src !
n 0 gt {
src dest storage n 1 sub hanoi
n . ( from ) print src . ( to ) print dest =
storage src dest n 1 sub hanoi
} if
end
} def
GS>/A /B /C 3 hanoi
1 from A to C
2 from A to B
1 from C to B
3 from A to C
1 from B to A
2 from B to C
1 from A to C
This is definitely more awkward than in most languages, but it doesn’t
seem prohibitive to me.
One major annoyance for interactive use is that, if execution aborts with
an error, all those local variable scopes are left on the stack. Might
be useful for debugging, I guess, but you can end up redefining things
in the wrong dictionary.
>> 5. Garbage-collection. Yes.
>
> Note that this was only added in PostScript level 2. In the original
> language implementation, memory management had to be done in (I think
> it’s called) mark-release style, using explicit calls to the “save”
> and “restore” functions.
Really? I had no idea! I always wondered why they thought `save` and
`restore` were worth incorporating. So that’s a way in which the
original PostScript was actually more like Forth, where you use `marker`
(or `forget`) to deallocate a chunk of the dictionary.
>> 6. Programs composed of expressions. Not really, but closer than any
>> of the currently popular languages.
>
> No distinction between “statements” and “expressions”.
Yes, that’s true, PostScript doesn’t have a statement/expression
distinction. But in that case we should award this Lispiness point to
Forth as well.
>> 7. A symbol type. [...]
>
> True enough.
>
> Java has the option for “interned” strings, which is a way of having a
> symbol type without having a symbol type, if you like.
That’s true, and in particular that’s useful if you’re implementing some
kind of language parser or interpreter in Java. Python has this too.
>> 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.
>
> In Lisp, the tree structure is apparent in the static syntax. In
> PostScript, the tree structure (such as it is) gets largely
> constructed dynamically at run time, and need not correspond to static
> syntax at all.
I’m not sure I agree. Here’s my example of tree-structured code in
PostScript, from the previous post:
GS>/sign {dup 0 gt {pop /positive} {0 lt {/negative} {/zero} ifelse} ifelse} def
This is an executable array of six things, the fourth of which is `{pop
/positive}`. To me, that’s *more* apparent from the static syntax than
the fact that in `(lambda (x y) (sqrt (+ (* x x) (* y y))))` the car of
the car of the cdr of the cdr is `sqrt`. The Lisp structure is a binary
tree, but the static syntax is formatted as an N-ary ordered tree. In
PostScript, the N-ary ordered tree of the static syntax corresponds to
the N-ary ordered tree in the PostScript data model.
(Note that this is another way in which PostScript is like Lisp but
unlike Forth: in Forths that expose their representation of threaded
code, it’s an array of xts, not a tree.)
There are *other* structures that get constructed dynamically at run
time in PostScript, such as the implicit gsave/grestore tree, the
call/return tree, the implicit begin/end tree, the implicit save/restore
tree — basically anything with a stack could be said to dynamically
construct a sort of tree structure at run time.
>> 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.
>
> Only insofar as every dynamic language “mostly belongs to the Lisp
> family”.
PostScript is a lot insofarther, I think. For example, Python, JS, and
Lua hit #1-5, but not #6-9, although they do have `eval`, so you can
compile code at runtime. None of them let you run code at compile time
or at read time. Perl4 is usually considered a dynamic language and
doesn’t have references at all, so we lose #4; Perl5 has references, but
distinguishes between the reference and the thing it refers to, so that
you can take references to references, so we still lose #4, but we gain
#9 because you can write things like Perligata. Tcl not only doesn’t
have references, it doesn’t even have dynamic typing; everything is a
string, but it sort of has #8 except that it doesn’t have a symbol type.
> [...]
>> the executable bit set by `cvx` is an attribute of references, not
>> objects.
>
> The same is true of read and write access bits -- except for
> dictionaries.
I had no idea!
> I remember doing some experiments with an Apple LaserWriter and a
> Macintosh II back in the late 1980s. Someone had cracked the
> encryption of the “eexec” operator, and we used this to discover that
> there was this other thing called “cexec” (only allowed within
> encrypted “eexec” execution, which is why you never saw it in public
> code) that let you load MC68000 machine code into the printer and hook
> into an API provided by the PostScript interpreter to manipulate
> PostScript objects and make function calls.
Wow, that’s amazing! I only ever knew that there was some controversy
about extracting fonts. Maybe this is how it was done?
> [...] The Macintosh compilers already generated MC68000 machine code,
> and I was able to whip up build scripts to link that code into the
> right format, with appropriately-set-up header fields, so that the
> printer would load and execute it.
What a great feeling!
Kragen
Back to comp.lang.forth | Previous | Next — Previous in thread | Next in thread | Find similar | Unroll 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