• Postscript in 4tH

    From Hans Bezemer@the.beez.speaks@gmail.com to comp.lang.forth on Sat Sep 5 19:55:35 2026
    From Newsgroup: comp.lang.forth

    Both raster graphics as well as SVG vector graphics have been available
    in 4tH for some time now. However, people have expressed a need for
    Postscript graphics.

    The next release of 4tH will contain a set of libraries that cover every single functionality of the already available SVG libraries.

    Note that existing SVG programs can easily be converted to Postscript by simply including the correct libraries and replace "SVGopen" by "PSopen"
    and "SVGclose" by "PSclose".

    No other changes are required.

    Hans Bezemer
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Paul Rubin@no.email@nospam.invalid to comp.lang.forth on Sat Sep 5 15:54:32 2026
    From Newsgroup: comp.lang.forth

    Hans Bezemer <the.beez.speaks@gmail.com> writes:
    Note that existing SVG programs can easily be converted to Postscript
    by simply including the correct libraries and replace "SVGopen" by
    "PSopen" and "SVGclose" by "PSclose".

    That's interesting as Postscript itself is a rather broken version of
    Forth. Do you mean with SVG calls you can do all the usual Postscript
    stuff including text rendering?

    I just wrote some Postscript code recently and hit some ridiculous snags
    due to dumb stuff in the language. They were fixable but "traps for the unwary" as it were.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From albert@albert@spenarnc.xs4all.nl to comp.lang.forth on Sun Sep 6 11:08:12 2026
    From Newsgroup: comp.lang.forth

    In article <871pb7l36f.fsf@nightsong.com>,
    Paul Rubin <no.email@nospam.invalid> wrote:
    Hans Bezemer <the.beez.speaks@gmail.com> writes:
    Note that existing SVG programs can easily be converted to Postscript
    by simply including the correct libraries and replace "SVGopen" by
    "PSopen" and "SVGclose" by "PSclose".

    That's interesting as Postscript itself is a rather broken version of
    Forth. Do you mean with SVG calls you can do all the usual Postscript
    stuff including text rendering?

    I just wrote some Postscript code recently and hit some ridiculous snags
    due to dumb stuff in the language. They were fixable but "traps for the >unwary" as it were.

    Maybe you weren't fully embracing the language. Don't expect it
    to be Forth.

    I have generated graphics as charts for an evacuation plan for the
    Dutch Harbour in case of calamities. It was easy to incorporate the
    whole map for the harbour in this, that were originally given in an
    other svg language. Generating PostScript from C.

    Also my assembler package includes generating instruction tables
    from the assembler itself. Generating PostScript from Forth.

    I experienced no "ridiculous snags". Actually the language is
    kind of nice and safer than Forth.

    Groetjes Albert
    --
    The Chinese government is satisfied with its military superiority over USA.
    The next 5 year plan has as primary goal to advance life expectancy
    over 80 years, like Western Europe.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Hans Bezemer@the.beez.speaks@gmail.com to comp.lang.forth on Sun Sep 6 14:27:09 2026
    From Newsgroup: comp.lang.forth

    On 06-09-2026 00:54, Paul Rubin wrote:
    Hans Bezemer <the.beez.speaks@gmail.com> writes:
    Note that existing SVG programs can easily be converted to Postscript
    by simply including the correct libraries and replace "SVGopen" by
    "PSopen" and "SVGclose" by "PSclose".

    That's interesting as Postscript itself is a rather broken version of
    Forth. Do you mean with SVG calls you can do all the usual Postscript
    stuff including text rendering?

    I just wrote some Postscript code recently and hit some ridiculous snags
    due to dumb stuff in the language. They were fixable but "traps for the unwary" as it were.

    First you have to realize those libraries aren't supposed to be a full Postscript implementation, but rather a useful subset of the language
    that will allow you to perform most basic tasks -- like:
    - Drawing pixels (yes!)
    - Drawing lines, circles, half circles, ellipses, polylines, polygons, rectangles, stuff like that;
    - Adding basic texts to those drawings.

    In addition, it can do 3D graphs and Turtle graphs -- both in SVG and PS
    -- and yes, with only minor changes to the code. E.g. this is a basic PS example by Anton Ertl, converted to those libraries:

    ---8<---
    \ example of how to do graphics by outputting Postscript (to Ghostscript)

    \ This program is in the public domain. No Warranty.
    \ Author: Anton Ertl
    \ 4tH conversion: Hans Bezemer

    \ this example just draws an sine wave. You could also do this
    \ directly in Postscript.

    \ For learning Postscript, look at the appropriate books or websites;
    \ this program only uses very little and the comments don't explain
    \ that.

    include lib/psgraph.4th
    include lib/pspoly.4th
    include lib/fp1.4th
    include lib/zenfsin.4th

    [UNDEFINED] y' [IF]
    : y' negate pic_height @ + ; ( y -- y')
    [THEN]

    : init-ps
    s" psine.ps" PSopen abort" Cannot open 'psine.ps'"
    512 256 canvas white whiteout black color
    polyline[ ;

    : data2coord ( f: rx f: ry -- rx1 ry1 )
    \ transform from our data to Postscript coordinates
    fswap 20 s>f f* 250 s>f f+ fswap
    100 s>f f* 125 s>f f+ ;

    : next-point ( f: rx f: ry -- )
    fround f>s >r fround f>s r> y' swap +point ;

    : finish-line ( -- )
    \ only stroke actually draws the line, so this is a good time to
    \ flush the output: earlier there is little effect, later delays
    \ displaying the line
    ]poly PSclose ;

    : sin-lines ( -- )
    100 -100 do
    i s>f 10 s>f f/ fdup fsin data2coord next-point
    loop ;

    : sin-graph ( -- )
    init-ps sin-lines finish-line ;

    \ We could now do the following to get postscript output (e.g., for
    \ redirecting it to a file or a printer):
    sin-graph
    ---8<---

    This is the PS code generated by this program:

    ---8<---
    %!PS-Adobe-3.0
    %%Creator: 4tH PostScript library
    %%Pages: 1
    %%BoundingBox: 0 0 512 256
    %%EndComments
    0.75 setlinewidth
    /Courier findfont
    11 scalefont
    setfont

    % Example Usage:
    /ellipse { % usage: X Y Rx Ry ellipse
    matrix currentmatrix % save current transformation matrix
    5 1 roll % roll parameters out of the way
    translate % move to center X Y
    scale % scale by Rx Ry
    0 0 1 0 360 arc % draw unit circle
    setmatrix % restore original transformation matrix
    } def

    % Macro definition: paints a rectangle
    % Stack X Y width height
    /rectangle {
    newpath
    4 2 roll % w h x y
    moveto % w h
    1 index 0 rlineto % w h
    0 exch rlineto % w
    neg 0 rlineto % --
    closepath
    } def

    1.0000 1.0000 1.0000 setrgbcolor
    0 0 512 256 rectfill
    0.0000 0.0000 0.0000 setrgbcolor
    newpath
    50 179 moveto
    52 171 lineto
    54 162 lineto
    56 152 lineto
    58 142 lineto
    60 132 lineto
    62 123 lineto
    64 113 lineto
    66 103 lineto
    68 93 lineto
    70 84 lineto
    72 75 lineto
    74 67 lineto
    76 59 lineto
    78 52 lineto
    80 45 lineto
    82 40 lineto
    84 35 lineto
    86 31 lineto
    88 28 lineto
    90 26 lineto
    92 25 lineto
    94 25 lineto
    96 26 lineto
    98 28 lineto
    100 31 lineto
    102 35 lineto
    104 40 lineto
    106 46 lineto
    108 52 lineto
    110 59 lineto
    112 67 lineto
    114 76 lineto
    116 85 lineto
    118 94 lineto
    120 103 lineto
    122 113 lineto
    124 123 lineto
    126 133 lineto
    128 143 lineto
    130 153 lineto
    132 162 lineto
    134 171 lineto
    136 180 lineto
    138 188 lineto
    140 196 lineto
    142 202 lineto
    144 208 lineto
    146 213 lineto
    148 218 lineto
    150 221 lineto
    152 223 lineto
    154 225 lineto
    156 225 lineto
    158 224 lineto
    160 223 lineto
    162 220 lineto
    164 217 lineto
    166 212 lineto
    168 207 lineto
    170 201 lineto
    172 194 lineto
    174 186 lineto
    176 178 lineto
    178 169 lineto
    180 160 lineto
    182 151 lineto
    184 141 lineto
    186 131 lineto
    188 121 lineto
    190 111 lineto
    192 101 lineto
    194 92 lineto
    196 82 lineto
    198 73 lineto
    200 65 lineto
    202 57 lineto
    204 50 lineto
    206 44 lineto
    208 39 lineto
    210 34 lineto
    212 30 lineto
    214 28 lineto
    216 26 lineto
    218 25 lineto
    220 25 lineto
    222 26 lineto
    224 29 lineto
    226 32 lineto
    228 36 lineto
    230 41 lineto
    232 47 lineto
    234 53 lineto
    236 61 lineto
    238 69 lineto
    240 77 lineto
    242 86 lineto
    244 95 lineto
    246 105 lineto
    248 115 lineto
    250 125 lineto
    252 135 lineto
    254 145 lineto
    256 155 lineto
    258 164 lineto
    260 173 lineto
    262 181 lineto
    264 189 lineto
    266 197 lineto
    268 203 lineto
    270 209 lineto
    272 214 lineto
    274 218 lineto
    276 221 lineto
    278 224 lineto
    280 225 lineto
    282 225 lineto
    284 224 lineto
    286 222 lineto
    288 220 lineto
    290 216 lineto
    292 211 lineto
    294 206 lineto
    296 200 lineto
    298 193 lineto
    300 185 lineto
    302 177 lineto
    304 168 lineto
    306 158 lineto
    308 149 lineto
    310 139 lineto
    312 129 lineto
    314 119 lineto
    316 109 lineto
    318 99 lineto
    320 90 lineto
    322 81 lineto
    324 72 lineto
    326 64 lineto
    328 56 lineto
    330 49 lineto
    332 43 lineto
    334 38 lineto
    336 33 lineto
    338 30 lineto
    340 27 lineto
    342 26 lineto
    344 25 lineto
    346 25 lineto
    348 27 lineto
    350 29 lineto
    352 32 lineto
    354 37 lineto
    356 42 lineto
    358 48 lineto
    360 54 lineto
    362 62 lineto
    364 70 lineto
    366 79 lineto
    368 88 lineto
    370 97 lineto
    372 107 lineto
    374 117 lineto
    376 127 lineto
    378 137 lineto
    380 147 lineto
    382 156 lineto
    384 165 lineto
    386 174 lineto
    388 183 lineto
    390 191 lineto
    392 198 lineto
    394 204 lineto
    396 210 lineto
    398 215 lineto
    400 219 lineto
    402 222 lineto
    404 224 lineto
    406 225 lineto
    408 225 lineto
    410 224 lineto
    412 222 lineto
    414 219 lineto
    416 215 lineto
    418 210 lineto
    420 205 lineto
    422 198 lineto
    424 191 lineto
    426 183 lineto
    428 175 lineto
    430 166 lineto
    432 157 lineto
    434 147 lineto
    436 137 lineto
    438 127 lineto
    440 118 lineto
    442 108 lineto
    444 98 lineto
    446 88 lineto
    448 79 lineto
    stroke
    showpage
    %%EOF
    ---8<---

    And this is the SVG code:

    ---8<---
    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
    <svg width="512" height="256" viewBox="0 0 512 256" xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink">
    <rect width="100%" height="100%" fill="#FFFFFF" />
    <polyline points="50,77 52,85 54,94 56,104 58,114 60,124 62,133 64,143
    66,153 68,163 70,172 72,181 74,189 76,197 78,204 80,211
    82,216 84,221 86,225 88,228 90,230 92,231 94,231 96,230
    98,228 100,225 102,221 104,216 106,210 108,204 110,197 112,189
    114,180 116,171 118,162 120,153 122,143 124,133 126,123 128,113
    130,103 132,94 134,85 136,76 138,68 140,60 142,54 144,48
    146,43 148,38 150,35 152,33 154,31 156,31 158,32 160,33
    162,36 164,39 166,44 168,49 170,55 172,62 174,70 176,78
    178,87 180,96 182,105 184,115 186,125 188,135 190,145 192,155
    194,164 196,174 198,183 200,191 202,199 204,206 206,212 208,217
    210,222 212,226 214,228 216,230 218,231 220,231 222,230 224,227
    226,224 228,220 230,215 232,209 234,203 236,195 238,187 240,179
    242,170 244,161 246,151 248,141 250,131 252,121 254,111 256,101
    258,92 260,83 262,75 264,67 266,59 268,53 270,47 272,42
    274,38 276,35 278,32 280,31 282,31 284,32 286,34 288,36
    290,40 292,45 294,50 296,56 298,63 300,71 302,79 304,88
    306,98 308,107 310,117 312,127 314,137 316,147 318,157 320,166
    322,175 324,184 326,192 328,200 330,207 332,213 334,218 336,223
    338,226 340,229 342,230 344,231 346,231 348,229 350,227 352,224
    354,219 356,214 358,208 360,202 362,194 364,186 366,177 368,168
    370,159 372,149 374,139 376,129 378,119 380,109 382,100 384,91
    386,82 388,73 390,65 392,58 394,52 396,46 398,41 400,37
    402,34 404,32 406,31 408,31 410,32 412,34 414,37 416,41
    418,46 420,51 422,58 424,65 426,73 428,81 430,90 432,99
    434,109 436,119 438,129 440,138 442,148 444,158 446,168 448,177" fill="none" stroke="#000000" />
    </svg>
    ---8<---

    With some care, it is also possible to generate a raster representation
    of the same graph.

    Hans Bezemer
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Paul Rubin@no.email@nospam.invalid to comp.lang.forth on Mon Sep 7 09:24:26 2026
    From Newsgroup: comp.lang.forth

    albert@spenarnc.xs4all.nl writes:
    I experienced no "ridiculous snags". Actually the language is
    kind of nice and safer than Forth.

    One thing that bit me was that "def" creates or modifies an essentially
    global variable. I had expected from the tutorial that I read that it
    was more like a local, or a LET binding in Lisp terms. So I had to
    puzzle out that calling a function that set "x" to something, changed
    the value in the caller. The fix is to create a new dictionary and push
    it on the stack. This was a pain to figure out because of the
    near-total absence of a debugging environment.

    Another annoying thing was that instead of "30 x !" you say "/x 30 def"
    and that means, to save function args in variables, you have to use "/x
    exch def" instead of "def". But that was better documented so it mostly
    only caused a bit of code clutter.

    I wrote some Postscript a long time ago and it went ok at the time. For
    this more recent thing, I had forgotten everything about it and had to
    start from scratch.

    You are probably right that it gets more comfortable when you use it
    more. You're right that I didn't "fully embrace" it. I basically
    puzzled out the minimum amount to get my immediate thing done.

    Of course PS is safer than Forth since it's garbage collected and
    doesn't have raw pointers. That has the corresponding performance and
    memory cost but that's fine. OTOH it still seems designed for fairly
    low resource environments. I think the original host for it was the
    Apple LaserWriter with a 68000 cpu and around 1MB of memory but I guess
    most of the memory was used for fonts and stuff.

    I remember that the Adobe manuals were pretty good but I didn't use them
    this time. If I had to do something more serious in Postscript I'd
    probably read through the Adobe manuals again first.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From albert@albert@spenarnc.xs4all.nl to comp.lang.forth on Mon Sep 7 23:47:25 2026
    From Newsgroup: comp.lang.forth

    In article <87wlsxjah1.fsf@nightsong.com>,
    Paul Rubin <no.email@nospam.invalid> wrote:

    I remember that the Adobe manuals were pretty good but I didn't use them
    this time. If I had to do something more serious in Postscript I'd
    probably read through the Adobe manuals again first.

    I had the benefit of the original Adobe manual.
    It helped me a lot, but the Dutch translation was awful.
    For instance "set and reset" was translated by the Dutch equivalent of
    "set and set again". (but it cost f5 say 2.5 euro's, and it got reimbursed
    by the client.)

    Groetjes Albert
    --
    The Chinese government is satisfied with its military superiority over USA.
    The next 5 year plan has as primary goal to advance life expectancy
    over 80 years, like Western Europe.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Kragen Javier Sitaker@kragen@canonical.org to comp.lang.forth,comp.lang.postscript on Tue Sep 8 02:09:43 2026
    From Newsgroup: comp.lang.forth

    albert@spenarnc.xs4all.nl writes:
    In article <871pb7l36f.fsf@nightsong.com>,
    Paul Rubin <no.email@nospam.invalid> wrote:
    That's interesting as Postscript itself is a rather broken version of >>Forth. Do you mean with SVG calls you can do all the usual Postscript >>stuff including text rendering?

    I just wrote some Postscript code recently and hit some ridiculous snags >>due to dumb stuff in the language. They were fixable but "traps for the >>unwary" as it were.

    Maybe you weren't fully embracing the language. Don't expect it
    to be Forth.

    I think of PostScript as being Lisp, more or less, but with quasi-Forth
    syntax, and using arrays where Lisp uses linked lists. Maybe Lua or
    Python is a closer analogy: PostScript has separate array and dictionary
    types, like Python. But itrCOs extensible like Lisp or Forth.

    Aside from the Adobe manual, Paul Bourke has a great tutorial and
    reference at <https://paulbourke.net/dataformats/postscript/>.
    Nathan Laredo has an amazing collection of PostScript code at <https://postscriptcode.com/>, which is worth browsing through, and of
    course Don Lancaster has lots of interesting stuff at <https://www.tinaja.com/pssamp1.shtml> and nearby.

    Mitch Bradley wrote a Forth *in* PostScript: <https://dl.acm.org/doi/pdf/10.1145/122630.127247>

    Kragen
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Hans Bezemer@the.beez.speaks@gmail.com to comp.lang.forth on Tue Sep 8 17:18:20 2026
    From Newsgroup: comp.lang.forth

    BTW, this is the code for the raster version:

    ---8<---
    \ This program is in the public domain. No Warranty.
    \ Author: Anton Ertl
    \ 4tH conversion: Hans Bezemer

    \ this example just draws an sine wave.

    include lib/graphics.4th
    include lib/gpoly.4th
    include lib/fp1.4th
    include lib/zenfsin.4th

    : y' negate pic_height @ + ; ( y -- y')

    : init-ps
    512 256 canvas 255 whiteout black color
    polyline[ ;

    : data2coord ( f: rx f: ry -- rx1 ry1 )
    \ transform from our data to Postscript coordinates
    fswap 20 s>f f* 250 s>f f+ fswap
    100 s>f f* 125 s>f f+ ;

    : next-point ( f: rx f: ry -- )
    fround f>s >r fround f>s r> y' swap +point ;

    : finish-line ( -- )
    ]poly s" gsine.ppm" save_image ;

    : sin-lines ( -- )
    100 -100 do
    i s>f 10 s>f f/ fdup fsin data2coord next-point
    loop ;

    : sin-graph ( -- )
    init-ps sin-lines finish-line ;

    sin-graph
    ---8<---

    Hans Bezemer

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.forth,comp.lang.postscript on Tue Sep 8 20:34:07 2026
    From Newsgroup: comp.lang.forth

    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 doesnrCOt. Lisp needs macros, because it also has rCLspecial formsrCY for syntactic constructs that implement things like
    control constructs, declarations etc. PostScript doesnrCOt have rCLspecial formsrCY, since all these concepts are implemented as built-in
    functions. Which is also why it doesnrCOt need macros.

    Also, Forth never heard of homoiconicity ...
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Kragen Javier Sitaker@kragen@canonical.org to comp.lang.forth,comp.lang.postscript,alt.folklore.computers,comp.lang.lisp on Tue Sep 8 22:38:58 2026
    From Newsgroup: comp.lang.forth

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

    Lawrence DrCOOliveiro <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 doesnrCOt. 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 didnrCOt 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 GrahamrCOs rCLWhat Made Lisp DifferentrCY <https://paulgraham.com/diff.html>:

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

    2. A function type. rCLIn Lisp, functions are first class objects--
    theyrCOre 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.rCY Yes; PostScript goes even harder here than Lisp, because thererCOs 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. rCLIn 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.rCY

    PostScript does work this way, as does Smalltalk (not coincidentally,
    from the same lab, which was also heavily into Lisp). ItrCOs 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 couldnrCOt 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 theyrCOre binary trees, while in PostScript theyrCOre N-ary ordered trees.

    9. rCLThe whole language always availablerCY, 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 itrCOs 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 yourCOre not familiar with PostScript, you might be puzzled by #8.
    LetrCOs 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 thatrCOs 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 rCLnamerCY, 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
    canrCOt 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 yourCOre familiar with old Lisp systems, almost all of this will seem
    very familiar, but the syntax will seem backwards, and yourCOll 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] isnrCOt 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 isnrCOt 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.
    ThatrCOs 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 rCLmicroanalysisrCY (profiling) paper, which also included some Algol as pseudocode); Lisp as the
    pseudocode, though without naming it (in KornfieldrCOs paper about rCLcombinatorially implosiverCY algorithms); none (in the geometric
    algorithm paper); Fortran (in the gamma-deviate-generation comment); and
    Ada (in the rCLACM ForumrCY letters column.) ItrCOs 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 itrCOs 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.

    ThatrCOs just the first 41 pages; IrCOll leave the other 490 pages to you.

    Kragen
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.forth,comp.lang.postscript,alt.folklore.computers,comp.lang.lisp on Wed Sep 9 03:05:28 2026
    From Newsgroup: comp.lang.forth

    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 didnrCOt have to be loaded into a PostScript object by way of
    the PostScript parser.

    ItrCOs just a function. ItrCOs like saying Python has rCLreadmacrosrCY because the JSON and XML library modules have functions for reading input
    streams and returning language-object representations of those
    structures.

    2. A function type. rCLIn Lisp, functions are first class objects--
    theyrCOre 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.rCY Yes; PostScript goes even harder here than
    Lisp, because thererCOs 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.

    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.

    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.

    4. A new concept of variables. rCLIn 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.rCY

    PostScript does work this way, as does Smalltalk (not
    coincidentally, from the same lab, which was also heavily into
    Lisp). ItrCOs 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.

    Fair enough.

    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
    itrCOs called) mark-release style, using explicit calls to the rCLsaverCY
    and rCLrestorerCY functions.

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

    No distinction between rCLstatementsrCY and rCLexpressionsrCY.

    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.

    True enough.

    Java has the option for rCLinternedrCY strings, which is a way of having a symbol type without having a symbol type, if you like.

    8. A notation for code using trees of symbols. Yes. The only
    difference is that in conventional Lisp theyrCOre binary trees, while
    in PostScript theyrCOre 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.

    9. rCLThe whole language always availablerCY, i.e., arbitrarily
    overlapping compile time and runtime. Yes.

    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 itrCOs pretty clear that it mostly belongs to
    the Lisp family.

    Only insofar as every dynamic language rCLmostly belongs to the Lisp
    familyrCY.

    Its resemblance to Lisp is much deeper than just being homoiconic.

    As you pointed out, other dynamic languages had their own versions
    of these features.

    Paul GrahamrCOs list is misleading, because it misses out things that
    make Lisp distinctive -- perhaps because they were not so well
    appreciated in the days before other dynamic languages became popular.

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

    Leaves out homoiconicity, which is such a fundamental thing.

    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 isnrCOt an
    established practice.

    Unnecessary, because it just doesnrCOt buy you much in functionality,
    the way it does in Lisp.

    This should be a clue as to how PostScript is very much different
    from Lisp-like languages.

    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. ThatrCOs why making `square` executable required calling `def` again rather than just `cvx`.

    The same is true of read and write access bits -- except for
    dictionaries.

    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 rCLeexecrCY operator, and we used this to discover that
    there was this other thing called rCLcexecrCY (only allowed within
    encrypted rCLeexecrCY 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.

    This effort was triggered by the existence of an app called
    rCLLaserTalkrCY, which did something truly magical and supposedly
    impossible: you could use it to send PostScript code to the printer,
    and get back the high-resolution rendered image as a file.

    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.

    All this cexec/eexec stuff went away in PostScript level 2 and later
    -- along with the dependency on Motorola-68K-family processors.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Johann 'Myrkraverk' Oskarsson@johann@myrkraverk.invalid to comp.lang.forth,comp.lang.postscript,alt.folklore.computers,comp.lang.lisp on Wed Sep 9 15:15:18 2026
    From Newsgroup: comp.lang.forth

    On 09/09/2026 9:38 AM, Kragen Javier Sitaker wrote:
    (Please forgive in advance the LLM-like wording of what follows. I
    wrote it entirely by hand, including reading the documents I linked, but IrCOve been coaxing


    Thank you for that wonderful exposition of PostScript, I'm almost tempt-
    ed to write my very own P.S. interpreter, but that's going to have to
    wait a better time, as I already have n+1 projects.

    And don't worry about sounding like an L.L.M. After all, they were
    trained on the best of us, and sound like us -- good writers -- more
    than we sound like them. That's how time works.

    I take it you've been coaxing Lawrence, but that's a waste of time, just
    fire away and forget about it; I don't care, and Lawrence doesn't care
    either.


    Best wishes, and happy coaxing Lawrence!
    --
    Johann | email: invalid -> com | http://www.myrkraverk.com/blog/
    I'm not from the Internet, I just work there. | via Easynews.com https://bsky.app/profile/myrkraverk.bsky.social | for ( ;; ) _:;
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From anton@anton@mips.complang.tuwien.ac.at (Anton Ertl) to comp.lang.forth,comp.lang.postscript on Wed Sep 9 06:42:05 2026
    From Newsgroup: comp.lang.forth

    Lawrence =?iso-8859-13?q?D=FFOliveiro?= <ldo@nz.invalid> writes:
    Lisp needs macros, because it also has
    "special forms" for syntactic constructs that implement things like
    control constructs, declarations etc. PostScript doesnrCOt have "special >forms", since all these concepts are implemented as built-in
    functions. Which is also why it doesnrCOt need macros.

    You cannot get around this kind of stuff. Postscript has a subparser
    for executable arrays ({ ... }) that behaves completely differently
    from the regular parser that is also used to build literal arrays ([
    ... ]). Forth has interpret state and compile state. The regular
    Postscript parser can be considered to be similar to interpret state
    (operators are executed), while the parser for executable arrays is
    similar to compile state (operators become part of the array).

    In Lisp, if you have a function like +, it's operands are evaluated
    and then passed to the function (comparable to interpret state),
    whereas a special form like defun (or whatever it's name is in your
    dialect of Lisp), the components are not evaluated by the REPL, but
    passed uninterpreted to the special form.

    Also, Forth never heard of homoiconicity ...

    The traditional indirect-threaded-code implememtations can be
    considered homoiconic, and I wrote a word in the 1980s that made use
    of this property to show a call graph of Forth words. Also,
    decompilers made use of this property.

    When Forth implementations became more advanced and switched to native
    code, some (commercial) implementations abandoned that feature (paying custormers pay to have the Forth vendor work on the innards, or at
    least that's what the vendor's think; presentations by Nick Nelson,
    one of the paying customers, make me think differently), while others
    still have similar stuff as an intermediate representation that is
    used for inlining and sometimes decompilation; one commercial
    implementation probably also has an intermediate representation for
    inlining, but I have not seen it using it for decompilation.

    These days, one could consider the sequence of translations (in the
    sense of the recognizer proposal <https://forth-standard.org/proposals/recognizer-committee-proposal-2025-09-11?hideDiff#reply-1686>)
    of a Forth program to be the homoiconic representation, except for one
    problem: translations may parse, and the translation returned from
    REC-STRING often does parse.

    However, after the translation, we do not have a central point for
    collecting the data; one might consider the sequence of calls made by
    the translations to EXECUTE COMPILE, LITERAL SLITERAL etc. (combined
    with their arguments) to be a homoiconic representation, but the
    question is where the "etc." above stops.

    - anton
    --
    M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
    comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
    New standard: https://forth-standard.org/
    EuroForth 2026 CFP: http://www.euroforth.org/ef26/cfp.html
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From anton@anton@mips.complang.tuwien.ac.at (Anton Ertl) to comp.lang.forth,comp.lang.postscript,alt.folklore.computers,comp.lang.lisp on Wed Sep 9 07:35:21 2026
    From Newsgroup: comp.lang.forth

    Kragen Javier Sitaker <kragen@canonical.org> writes:
    PostScript has several other key similarities to Lisp. Referencing Paul >Graham's "What Made Lisp Different" <https://paulgraham.com/diff.html>:

    Given your claim below that Forth has only #1, #3, #9, let's look at
    the others:

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

    Forth has execution tokens.

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

    In Forth, neither variables nor values have types that are known to
    the Forth system. The way to use that is that the programmer must
    know the type, and that typically means always storing the same type
    into a variable.

    5. Garbage-collection.

    Not built into Forth.

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

    Forth is like Postscript here: Programs composed of words (operators
    in Postscript), which work on values on the stack.

    7. A symbol type. Yes, PostScript distinguishes /names from strings,

    Forth distinguishes name tokens from strings, but they do not play the
    role they play in Lisp or Postscript; in particular, you have to
    create new named words outside a colon definitions, and if you use a
    named word to get a unique value, you tend to use its body address,
    not it's name token.

    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.

    As mentioned in <2026Sep9.084205@mips.complang.tuwien.ac.at>, one
    might consider the indirect-threaded code representation and its
    modern equivalents for inlining to be such a notation, but in Forth
    many control structures become branches, whereas in Postscript the
    controlled stuff is a procedure (in Forth terminology, a quotation).

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

    And also #2, and partially #6, #7, and #8.

    Followups reduced to comp.lang.forth. Add some other groups if you
    really have to.

    - anton
    --
    M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
    comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
    New standard: https://forth-standard.org/
    EuroForth 2026 CFP: http://www.euroforth.org/ef26/cfp.html
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.forth,comp.lang.postscript,alt.folklore.computers,comp.lang.lisp on Wed Sep 9 08:10:15 2026
    From Newsgroup: comp.lang.forth

    On Wed, 09 Sep 2026 07:35:21 GMT, Anton Ertl wrote:

    In Forth, neither variables nor values have types that are known to
    the Forth system. The way to use that is that the programmer must
    know the type, and that typically means always storing the same type
    into a variable.

    Forth is not strongly-typed. IrCOm not aware of any high-level dynamic
    language that is not strongly-typed. At a bare minimum, you need to
    know what value is a pointer and what isnrCOt, otherwise memory
    management is not going to be a happy business.

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

    Forth is like Postscript here: Programs composed of words (operators
    in Postscript), which work on values on the stack.

    Note that procedures are also executable entities in PostScript. They
    exist apart from any name that might be referencing them (aka
    rCLlambdasrCY). And they can be constructed dynamically at run-time.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.forth,comp.lang.postscript on Wed Sep 9 08:28:18 2026
    From Newsgroup: comp.lang.forth

    On Wed, 09 Sep 2026 06:42:05 GMT, Anton Ertl wrote:

    On Tue, 8 Sep 2026 20:34:07 -0000 (UTC), Lawrence DrCOOliveiro wrote:

    Lisp needs macros, because it also has rCLspecial formsrCY for
    syntactic constructs that implement things like control constructs,
    declarations etc. PostScript doesnrCOt have rCLspecial formsrCY, since
    all these concepts are implemented as built-in functions. Which is
    also why it doesnrCOt need macros.

    You cannot get around this kind of stuff. Postscript has a subparser
    for executable arrays ({ ... }) that behaves completely differently
    from the regular parser that is also used to build literal arrays ([
    ... ]).

    I have implemented an actual PostScript-like language. The token
    interpreter core looks basically like this:

    def interpret(ctx, sym, symval : bytes) :
    "callback from lexer to interpret next symbol."
    ...
    a few dozen lines to do various symbol specific processing,
    culminating in the construction of a token object in the_obj
    ...
    if len(ctx.currentprocs) != 0 :
    ctx.currentprocs[-1].append(the_obj)
    else :
    ctx.execute(the_obj, doproc = False)
    #end if
    #end interpret

    So the only difference in the parser between being inside a procedure definition (there is an entry in the ctx.currentprocs stack of
    in-progress procedure definitions) and outside it comes down to that
    last if-statement: if inside, append the just-parsed object (symbol,
    literal, nested procedure, whatever) to the current procedure
    definition; if outside, just execute it directly.

    Also, Forth never heard of homoiconicity ...

    The traditional indirect-threaded-code implememtations can be
    considered homoiconic, and I wrote a word in the 1980s that made use
    of this property to show a call graph of Forth words. Also,
    decompilers made use of this property.

    ThatrCOs a limited form of read-only introspection. You couldnrCOt do transformations on the AST or such, could you? For a start, that would
    require dynamic memory allocation, which Forth doesnrCOt have.

    Even Java could probably manage what Forth does, and nobody would call
    it rCLhomoiconicrCY.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Paul Rubin@no.email@nospam.invalid to comp.lang.forth,comp.lang.postscript,alt.folklore.computers,comp.lang.lisp on Wed Sep 9 03:01:07 2026
    From Newsgroup: comp.lang.forth

    Kragen Javier Sitaker <kragen@canonical.org> writes:
    At the time that PostScript was designed, most Lisps did not have
    macros; they still had fexprs.... PostScript first shipped in 01982,

    1982 was right in Lisp's heyday and I expect the major implementations
    all had macros (Maclisp, Lisp Machine Lisp and its descendants, Nil, Spicelisp). Scheme was around and didn't have macros but I don't think
    it had fexprs either.

    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 itrCOs pretty clear that it mostly belongs to the Lisp family.

    I think that list is pretty bogus. The main characteristic of Lisp imho
    is lambda calculus and lambda binding. In order to even have a local
    variable in a PostScript function, you have to literally push a
    dictionary on the evaluation stack and invoke BEGIN, then later END to
    pop the dictionary stack, like Forth wordlists.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From anton@anton@mips.complang.tuwien.ac.at (Anton Ertl) to comp.lang.forth,comp.lang.postscript on Wed Sep 9 10:03:13 2026
    From Newsgroup: comp.lang.forth

    Lawrence =?iso-8859-13?q?D=FFOliveiro?= <ldo@nz.invalid> writes:
    On Wed, 09 Sep 2026 06:42:05 GMT, Anton Ertl wrote:
    You cannot get around this kind of stuff. Postscript has a subparser
    for executable arrays ({ ... }) that behaves completely differently
    from the regular parser that is also used to build literal arrays ([
    ... ]).
    [...]
    if len(ctx.currentprocs) != 0 :
    ctx.currentprocs[-1].append(the_obj)
    else :
    ctx.execute(the_obj, doproc = False)
    #end if
    #end interpret

    So the only difference in the parser between being inside a procedure >definition (there is an entry in the ctx.currentprocs stack of
    in-progress procedure definitions) and outside it comes down to that
    last if-statement: if inside, append the just-parsed object (symbol,
    literal, nested procedure, whatever) to the current procedure
    definition; if outside, just execute it directly.

    Sounds very much like compile state and interpret state.

    The traditional indirect-threaded-code implememtations can be
    considered homoiconic, and I wrote a word in the 1980s that made use
    of this property to show a call graph of Forth words. Also,
    decompilers made use of this property.

    ThatrCOs a limited form of read-only introspection. You couldnrCOt do >transformations on the AST or such, could you?

    Changing the compiled code was not common, but used. Even development
    Gforth has:

    |'replace-word' ( xt1 xt2 - ) gforth-1.0
    | make xt2 do xt1, both need to be colon definitions

    However, the more common approach is to change the source code and
    load it again (typically in a new session, or after a FORGET or a
    marker). And in Postscript and Lisp that's also the case.

    And consequently, this feature of indirect-threaded code has been
    abandoned by more advanced Forth systems. E.g., in Gforth, which
    still has a threaded-code substrate, writing to some threaded-code
    locations has no effect in the engines gforth and gforth-fast. If you
    want to do such things, use the engine gforth-itc (for
    indirect-threaded code).

    For a start, that would
    require dynamic memory allocation, which Forth doesnrCOt have.

    Forth has what is usually understood under "dynamic memory
    allocation", but it's not clear why that should be required for
    writing to code.

    - anton
    --
    M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
    comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
    New standard: https://forth-standard.org/
    EuroForth 2026 CFP: http://www.euroforth.org/ef26/cfp.html
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From albert@albert@spenarnc.xs4all.nl to comp.lang.forth on Wed Sep 9 14:05:08 2026
    From Newsgroup: comp.lang.forth

    In article <87o6e7cifh.fsf@debian>,
    Kragen Javier Sitaker <kragen@canonical.org> wrote:
    (Please forgive in advance the LLM-like wording of what follows. I
    wrote it entirely by hand, including reading the documents I linked, but >IrCOve been coaxing

    Lawrence DrCOOliveiro <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 doesnrCOt. 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 didnrCOt 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.

    I was thinking about a simplified Forth where the { .. } is a behaviour,
    say a lambda. (Actually it works more or less.)
    : SQUARE DUP * ;
    becomes
    { DUP * } : SQUARE ; Squaring (n -- n^2)

    : CONSTANT CREATE , DOES> @ ;
    becomes
    { , } { @ } META CONSTANT ; execute NETA, add CONSTANT to dictionary.

    It does away with idosyncratic stuff like:
    :NONAME ; CREATE DOES> [: ;]
    ; replaces \ .


    <SNIP>
    Kragen
    --
    The Chinese government is satisfied with its military superiority over USA.
    The next 5 year plan has as primary goal to advance life expectancy
    over 80 years, like Western Europe.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From albert@albert@spenarnc.xs4all.nl to comp.lang.forth,comp.lang.postscript on Wed Sep 9 14:36:04 2026
    From Newsgroup: comp.lang.forth

    In article <2026Sep9.084205@mips.complang.tuwien.ac.at>,
    Anton Ertl <anton@mips.complang.tuwien.ac.at> wrote:
    Lawrence =?iso-8859-13?q?D=FFOliveiro?= <ldo@nz.invalid> writes:
    <SNIP>
    Also, Forth never heard of homoiconicity ...

    The traditional indirect-threaded-code implememtations can be
    considered homoiconic, and I wrote a word in the 1980s that made use
    of this property to show a call graph of Forth words. Also,
    decompilers made use of this property.

    ciforth (computer intelligence Forth) was originally intended
    to be a substrate for an intelligence. One of the first design
    goals is to understand itself.
    I didn't known the term homo-iconic, but is is similar.
    For example the ci would write a function and then analyse and speed
    up the function. So replacing functions as in actual brain.
    (A ci thinks for herself and formulates goals to understand the world
    better. The idea is to live in the real world, so giving her internet
    access.)
    I'm working on an optimiser that leans on this, starting from
    assigning properties to individual words that make it possible
    All AI aspirations are abandoned, but a reasonable optimiser is
    within reach.

    See also
    https://home.hccnet.nl/a.w.m.van.der.horst/forthlecture6.html


    - anton
    --
    The Chinese government is satisfied with its military superiority over USA.
    The next 5 year plan has as primary goal to advance life expectancy
    over 80 years, like Western Europe.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From albert@albert@spenarnc.xs4all.nl to comp.lang.forth,comp.lang.postscript on Wed Sep 9 14:42:10 2026
    From Newsgroup: comp.lang.forth

    In article <117r5b2$u52n$5@dont-email.me>,
    Lawrence D Oliveiro <ldo@nz.invalid> wrote:
    <SNIP>
    ThatrCOs a limited form of read-only introspection. You couldnrCOt do >transformations on the AST or such, could you? For a start, that would >require dynamic memory allocation, which Forth doesnrCOt have.

    Infinite memory is as good as dynamic memory. I can expand my Forth
    to e.g. 128 Gbyte that is for all practical purposes infinite.
    Then it is a small change to use ALLOCATE that is not in the kernel
    of my Forth, but it is a loadable extension.


    Even Java could probably manage what Forth does, and nobody would call
    it rCLhomoiconicrCY.
    --
    The Chinese government is satisfied with its military superiority over USA.
    The next 5 year plan has as primary goal to advance life expectancy
    over 80 years, like Western Europe.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From antispam@antispam@fricas.org (Waldek Hebisch) to comp.lang.forth,comp.lang.postscript on Wed Sep 9 19:17:06 2026
    From Newsgroup: comp.lang.forth

    In comp.lang.forth albert@spenarnc.xs4all.nl wrote:
    In article <117r5b2$u52n$5@dont-email.me>,
    Lawrence D|+Oliveiro <ldo@nz.invalid> wrote:
    <SNIP>
    That|o??s a limited form of read-only introspection. You couldn|o??t do >>transformations on the AST or such, could you? For a start, that would >>require dynamic memory allocation, which Forth doesn|o??t have.

    Infinite memory is as good as dynamic memory. I can expand my Forth
    to e.g. 128 Gbyte that is for all practical purposes infinite.

    Maybe it is enough for use with Forth. I have a computation that
    runs out of memory in IIRC 100 GB. I do not think giving it full
    128 GB would make a big difference. OTOH I am pretty sure that
    this computation is finite, I simply do not know how much it
    needs, maybe 256 GB, maybe terabyte.

    More important, if you do not reclaim memory you eventually will
    run out of it. And modern processors are fast, so it can happen
    faster than you suspect. Of course, this is matter of programming
    style. In Lisp program when I monitor memory use I see gigabytes
    allocated in seconds. Without GC after few minutes all 128 GB
    that I have would be gone.
    --
    Waldek Hebisch
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.forth,comp.lang.postscript on Wed Sep 9 21:54:17 2026
    From Newsgroup: comp.lang.forth

    On Wed, 09 Sep 2026 14:42:10 +0200, albert wrote:

    On Wed, 9 Sep 2026 08:28:18 -0000 (UTC), Lawrence DrCOOliveiro wrote:

    ThatrCOs a limited form of read-only introspection. You couldnrCOt do
    transformations on the AST or such, could you? For a start, that
    would require dynamic memory allocation, which Forth doesnrCOt have.

    Infinite memory is as good as dynamic memory. I can expand my Forth
    to e.g. 128 Gbyte that is for all practical purposes infinite. Then
    it is a small change to use ALLOCATE that is not in the kernel of my
    Forth, but it is a loadable extension.

    Interestingly enough, some Lisp machines behaved in a similar way.
    They lacked garbage collection. After memory became full, you took a
    snapshot of all the live objects in your machine state, and rebooted.
    Reloading the snapshot left all the garbage behind, so you were able
    to resume work from there, until memory filled up again.

    Apparently you could typically go for longer than a day before filling
    all of memory. Not sure if this was a sign of how large memories had
    become by then, or how slow Lisp machines were ...

    Still, you get the point that thatrCOs a crude way of managing memory.
    It is not good for handling long-running tasks.

    Also on modern multitasking systems, you might need to be running
    other things simultaneously, besides your Forth or Lisp program. So
    having the one process hog all available resources is ... not very
    sociable.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.forth,comp.lang.postscript on Wed Sep 9 22:17:56 2026
    From Newsgroup: comp.lang.forth

    On Wed, 09 Sep 2026 10:03:13 GMT, Anton Ertl wrote:

    On Wed, 9 Sep 2026 08:28:18 -0000 (UTC), Lawrence DrCOOliveiro wrote:

    On Wed, 09 Sep 2026 06:42:05 GMT, Anton Ertl wrote:

    You cannot get around this kind of stuff. Postscript has a
    subparser for executable arrays ({ ... }) that behaves completely
    differently from the regular parser that is also used to build
    literal arrays ([ ... ]).

    if len(ctx.currentprocs) != 0 :
    ctx.currentprocs[-1].append(the_obj)
    else :
    ctx.execute(the_obj, doproc = False)
    #end if
    #end interpret

    So the only difference in the parser between being inside a
    procedure definition (there is an entry in the ctx.currentprocs
    stack of in-progress procedure definitions) and outside it comes
    down to that last if-statement: if inside, append the just-parsed
    object (symbol, literal, nested procedure, whatever) to the current
    procedure definition; if outside, just execute it directly.

    Sounds very much like compile state and interpret state.

    You said the rCLsubparser ... behaves completely differently from the
    regular parser that is also used to build literal arraysrCY. I showed
    that it doesnrCOt.

    The traditional indirect-threaded-code implememtations can be
    considered homoiconic, and I wrote a word in the 1980s that made use
    of this property to show a call graph of Forth words. Also,
    decompilers made use of this property.

    ThatrCOs a limited form of read-only introspection. You couldnrCOt do
    transformations on the AST or such, could you?

    Changing the compiled code was not common, but used. ...

    In Lisp code, itrCOs very common.

    However, the more common approach is to change the source code and
    load it again (typically in a new session, or after a FORGET or a
    marker). And in Postscript and Lisp that's also the case.

    The difference being, PostScript and Lisp have a more efficient
    approach to dealing with the memory occupied by the deleted objects.

    For a start, that would require dynamic memory allocation, which
    Forth doesnrCOt have.

    Forth has what is usually understood under "dynamic memory
    allocation", but it's not clear why that should be required for
    writing to code.

    More efficient memory management. In a dynamic language, you are
    creating and deleting objects all the time, and not in a
    last-in-first-out order. So you need a heap mechanism of some sort to
    manage storage efficiently.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Kragen Javier Sitaker@kragen@canonical.org to comp.lang.forth,comp.lang.postscript,alt.folklore.computers,comp.lang.lisp on Thu Sep 10 01:06:45 2026
    From Newsgroup: comp.lang.forth

    (Apologies if this sounds like AI slop; IrCOve been interacting with
    Claude Opus 5 a lot today, and although I didnrCOt use any LLM to write
    any of this, its voice may be leaking into mine.)

    Lawrence DrCOOliveiro <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 didnrCOt have to be loaded into a PostScript object by way of
    the PostScript parser.

    ItrCOs just a function. ItrCOs like saying Python has rCLreadmacrosrCY because
    the JSON and XML library modules have functions for reading input

    ItrCOs a function that runs while the PostScript source code is being
    read, which means that it can change the interpretation of that source
    code. ThatrCOs a way that PostScript is like Lisp and unlike Python. PythonrCOs JSON and XML library modulesrCO 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. ThererCOs 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 doesnrCOt matter that `dup` is defined:

    GS>typeof oagjijgw ===
    nametype

    YourCOd 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 havenrCOt defined `oagjijgw` previously.

    That is how PostScriptrCOs `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. rCLIn 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 rCLhomoiconicrCY unless
    for some reason it lacks the ability to manipulate sequences of
    characters. So rCLhomoiconic languagesrCY is no longer a logically well-defined category; itrCOs more a sliding scale of what programmers
    find easy or difficult.

    But, look again at PGrCOs explanation of what he means by rCLa function typerCY:

    rCLIn Lisp, functions are first class objects-- theyrCOre 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.rCY

    The only one of these that plausibly relates to homoiconicity is rCLhave a literal representationrCY. 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 thatrCOs
    still a string representation! And *most* languages don't have the
    ability to print out, for example, struct literals.

    What makes a rCLstring literalrCY or rCLinteger literalrCY a *literal* is that you can put it in your source code without having to give it a name.

    C since C99 has rCLcompound literalsrCY 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>: rCLA struct literal denotes a newly allocated struct value by listing the values of its fields.rCY

    JS has rCLarray literalsrCY <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 PGrCOs rCLhas a literal representationrCY 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 itrCOs 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.

    HererCOs 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 doesnrCOt
    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
    itrCOs called) mark-release style, using explicit calls to the rCLsaverCY
    and rCLrestorerCY functions.

    Really? I had no idea! I always wondered why they thought `save` and `restore` were worth incorporating. So thatrCOs 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 rCLstatementsrCY and rCLexpressionsrCY.

    Yes, thatrCOs true, PostScript doesnrCOt 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 rCLinternedrCY strings, which is a way of having a symbol type without having a symbol type, if you like.

    ThatrCOs true, and in particular thatrCOs useful if yourCOre 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 theyrCOre binary trees, while
    in PostScript theyrCOre 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.

    IrCOm not sure I agree. HererCOs 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, thatrCOs *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, itrCOs 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 treerCerCorCebasically 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 itrCOs pretty clear that it mostly belongs to
    the Lisp family.

    Only insofar as every dynamic language rCLmostly belongs to the Lisp familyrCY.

    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
    doesnrCOt 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 doesnrCOt
    have references, it doesnrCOt even have dynamic typing; everything is a
    string, but it sort of has #8 except that it doesnrCOt 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 rCLeexecrCY operator, and we used this to discover that there was this other thing called rCLcexecrCY (only allowed within
    encrypted rCLeexecrCY 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, thatrCOs 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
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Kragen Javier Sitaker@kragen@canonical.org to comp.lang.forth,comp.lang.postscript,alt.folklore.computers,comp.lang.lisp on Thu Sep 10 01:11:36 2026
    From Newsgroup: comp.lang.forth

    Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.invalid> writes:
    Thank you for that wonderful exposition of PostScript, I'm almost tempt-
    ed to write my very own P.S. interpreter, but that's going to have to
    wait a better time, as I already have n+1 projects.

    Maybe you can coax an LLM to do it.

    Best wishes, and happy coaxing Lawrence!

    I choose to interpret this as rCLHappy coaxing, Lawrence!rCY, as an aside to him in a message to me, hoping that he is successful in coaxing whoever
    he needs to coax.

    Kragen
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Kragen Javier Sitaker@kragen@canonical.org to comp.lang.forth on Thu Sep 10 01:44:59 2026
    From Newsgroup: comp.lang.forth

    anton@mips.complang.tuwien.ac.at (Anton Ertl) writes:
    Kragen Javier Sitaker <kragen@canonical.org> writes:
    PostScript has several other key similarities to Lisp. Referencing Paul >>Graham's "What Made Lisp Different" <https://paulgraham.com/diff.html>:

    Given your claim below that Forth has only #1, #3, #9, let's look at
    the others:

    2. A function type. rCLIn Lisp, functions are first class objects-- >>theyrCOre 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.rCY

    Forth has execution tokens.

    I thought about this when posting. Xts certainly can be stored in
    variables or passed around as arguments or results. But orthodox Forth doesnrCOt have Joy-like quotations, which I think is the language feature
    that allows you to claim that functions rCLhave a literal representationrCY, although it does have `:noname`. Orthodox Forth is almost the same as C
    in this sense, in that you must define all of your functions at the top
    level. Function pointers such as Forth xts provide a very significant
    gain in expressiveness, but function *literals* add so much
    expressiveness that you can use them to define custom control
    structures, as in Smalltalk (or PostScript).

    Lisp programs also commonly create new functions at runtime, although in
    modern Lisp this is largely limited to creating closures. Forth has
    closures in the form of `create does>`, but because that appends to the dictionary, you donrCOt normally do it willy-nilly, usually only when you
    are loading a program.

    4. A new concept of variables. rCLIn 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.rCY

    In Forth, neither variables nor values have types that are known to
    the Forth system. The way to use that is that the programmer must
    know the type, and that typically means always storing the same type
    into a variable.

    That is certainly true, but to my eye, Forth is much more similar to
    assembly, Fortran, or C in this sense than to Lisp. You might `create`
    an array and give it a name, for example, and you can think of that name
    as referring to the beginning of the array (like a label in assembly
    language) or to the entire array (as in Fortran or C), but it definitely
    does not refer to a cell somewhere holding a *pointer to* the array, the
    way it does in Lisp and PostScript (or Python, or JS, or Lua, or
    Smalltalk, etc.)

    Forth uses the term `variable` for single-cell variables, and those are
    often used to store integers that are not pointers. Some Lisps (and,
    for example, Lua and Squeak) avoid rCLboxingrCY their integers in a memory allocation by packing type bits into a pointer, but this trick is
    invisible to the language semantics, while it is very visible in Forth.

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

    Forth is like Postscript here: Programs composed of words (operators
    in Postscript), which work on values on the stack.

    Yes. DrCOOliveiro suggested that what PG intended here was that thererCOs
    no distinction between statements and expresions, which is plausible;
    but, as I see it, in neither PostScript nor Forth do we have clear-cut expressions in the syntax, so even if PostScript (and Forth) fit what PG intended to say, they do not fit what he actually said.

    7. A symbol type. Yes, PostScript distinguishes /names from strings,

    Forth distinguishes name tokens from strings, but they do not play the
    role they play in Lisp or Postscript; in particular, you have to
    create new named words outside a colon definitions, and if you use a
    named word to get a unique value, you tend to use its body address,
    not its name token.

    I had forgotten about name tokens; thank you for the reminder.

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

    As mentioned in <2026Sep9.084205@mips.complang.tuwien.ac.at>, one
    might consider the indirect-threaded code representation and its
    modern equivalents for inlining to be such a notation, but in Forth
    many control structures become branches, whereas in Postscript the
    controlled stuff is a procedure (in Forth terminology, a quotation).

    As you say, Forth flattens the tree into an array at compile time.
    Also, though, it is entirely omitted from the standards, so that Forths
    that do not use threaded code can still count as rCLcompliantrCY.

    The things in the array are generally not name tokens, but execution
    tokens. PostScript supports either one, and people usually do `bind`
    their subroutines, replacing the symbols with subroutine pointers, so
    that they run faster:

    GS>/square {dup mul} def
    GS>4 square =
    16
    GS>/square load ===
    {dup mul}
    GS>/square load bind ===
    {--dup-- --mul--}

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

    And also #2, and partially #6, #7, and #8.

    Well, thatrCOs certainly a defensible position, but I think itrCOs still debatable.

    Thank you very much for your thought-provoking post.

    Kragen
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Paul Rubin@no.email@nospam.invalid to comp.lang.forth,comp.lang.postscript on Wed Sep 9 22:14:26 2026
    From Newsgroup: comp.lang.forth

    Lawrence DrCOOliveiro <ldo@nz.invalid> writes:
    Apparently you could typically go for longer than a day before filling
    all of memory. Not sure if this was a sign of how large memories had
    become by then, or how slow Lisp machines were ...

    Lisp code in those days was written to avoid unnecessary allocation.
    You'd use imperative-style RPLACA/RPLACD (aka setcar/setcdr) a lot,
    nreverse, nconc, that sort of thing. All not really in the Lisp spirit,
    I would say, but it reflected Lisp programming practices from even
    earlier and more limited machines.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@ldo@nz.invalid to comp.lang.forth,comp.lang.postscript,alt.folklore.computers,comp.lang.lisp on Thu Sep 10 07:38:19 2026
    From Newsgroup: comp.lang.forth

    On Thu, 10 Sep 2026 01:06:45 -0300, Kragen Javier Sitaker wrote:

    Lawrence DrCOOliveiro <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 didnrCOt have to be loaded into a
    PostScript object by way of the PostScript parser. >> >> ItrCOs just
    a function. ItrCOs like saying Python has rCLreadmacrosrCY because >>
    the JSON and XML library modules have functions for reading input

    ItrCOs a function that runs while the PostScript source code is being
    read, which means that it can change the interpretation of that
    source code.

    YourCOd 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 havenrCOt defined `oagjijgw` previously.

    How about this little program:

    import code
    import ast

    class TypeofExpander(ast.NodeTransformer) :

    def visit_Call(self, node) :
    result = node
    if isinstance(node.func, ast.Name) and node.func.id == "typeof" :
    result = ast.Constant(value = type(node.args[0]).__name__)
    else :
    result = type(node) \
    (
    func = node.func,
    args = list(self.visit(a) for a in node.args),
    keywords = list((a.arg, self.visit(a.value)) for a in node.keywords)
    )
    #end if
    return result
    #end visit_Call

    def visit_Expr(self, node) :
    return type(node)(self.visit(node.value))
    #end visit_Expr

    #end TypeofExpander

    console = code.InteractiveConsole()
    while True :
    line = input("? ")
    syntax_in = ast.parse(line, mode = "single")
    syntax_out = TypeofExpander().visit(syntax_in)
    ast.fix_missing_locations(syntax_out)
    code = compile(syntax_out, filename = "<console>", mode = "single")
    console.runcode(code)
    #end while

    Example run:

    ldo@theon:python_try> ./readmacro_fakeit
    ? 2 + 2
    4
    ? import math
    ? math.log
    <built-in function log>
    ? typeof(oagjijgw)
    'Name'
    ? print("typeof(", oagjijgw, ") is", typeof(oagjijgw))
    Traceback (most recent call last):
    File "<console>", line 1, in <module>
    NameError: name 'oagjijgw' is not defined
    ? print("typeof(oagjijgw) is", typeof(oagjijgw))
    typeof(oagjijgw) is Name
    ? print("typeof(", math.log, ") is", typeof(math.log))
    typeof( <built-in function log> ) is Attribute

    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.

    Except that ...

    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.

    So this is no longer a similarity between PostScript and Lisp.

    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:

    OK, so the definition of rCLhomoiconicrCY becomes rCLthe AST *can* be represented in terms of language objectsrCY, not rCLthe AST *is*
    represented in terms of language objectsrCY. I can accept that.

    Unfortunately, this definition of homoiconicity suggests that any
    language compiled from sequences of characters is rCLhomoiconicrCY
    unless for some reason it lacks the ability to manipulate sequences
    of characters.

    I donrCOt see why that should be rCLunfortunaterCY for the definition (for
    the language -- thatrCOs another matter). But I think a sequence of text characters is too low-level and unstructured a representation to be
    worthy of the name rCLhomoiconicrCY, anyway. It definitely has to involve
    the AST level in some way.

    But, look again at PGrCOs explanation of what he means by rCLa function typerCY:

    rCLIn Lisp, functions are first class objects-- theyrCOre 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.rCY

    The only one of these that plausibly relates to homoiconicity is
    rCLhave a literal representationrCY. 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 thatrCOs still a string representation! And *most* languages don't have the ability to print out, for example, struct
    literals.

    Python can. If your class defines a method called rCL__repr__rCY, it will
    be called by the built-in repr() function, and is expected to return
    some string representation that should be usable to reconstruct the object.

    Then there is also the rCL__str__rCY method and and corresponding built-in str() function, which is just expected to return some convenient,
    reasonably descriptive string.

    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.

    HererCOs a slightly reformatted interactive session showing how
    awkward it is to simulate anything resembling local variables, in
    case anyone is curious:

    ...

    This is definitely more awkward than in most languages, but it
    doesnrCOt seem prohibitive to me.

    For comparison, hererCOs an example from my PostScript-alike:

    /Count 99 ddef

    /metatry
    { # provides context for nonlocals
    dup
    /Name exch ldef
    /Count 0 ldef
    { # actual proc
    /Count dup lload 1 add lstore
    /Count dup dload 1 add dstore
    Name =
    (local Count = ) print /Count lload =
    (global Count = ) print /Count dload =
    (whichever Count = ) print Count =
    }
    }
    ddef

    /try1 metatry ddef
    /try2 metatry ddef

    try1
    try2
    try1
    try2

    Note the use of ldef/lload/store for lexical binding, and
    ddef/dload/dstore for dynamic binding, in place of simple
    def/load/store in old PostScript. As a result, functions now actually
    become useful as first-class objects. The output is:

    try1
    local Count = 1
    global Count = 100
    whichever Count = 1
    try2
    local Count = 1
    global Count = 101
    whichever Count = 1
    try1
    local Count = 2
    global Count = 102
    whichever Count = 2
    try2
    local Count = 2
    global Count = 103
    whichever Count = 2

    7. A symbol type. [...]

    True enough.

    Java has the option for rCLinternedrCY strings, which is a way of having a >> symbol type without having a symbol type, if you like.

    ThatrCOs true, and in particular thatrCOs useful if yourCOre implementing some
    kind of language parser or interpreter in Java. Python has this too.

    Python doesnrCOt need it, because simple rCL==rCY equality comparison works anyway, whereas it doesnrCOt in Java.

    8. A notation for code using trees of symbols. Yes. The only
    difference is that in conventional Lisp theyrCOre binary trees, while
    in PostScript theyrCOre 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.

    IrCOm not sure I agree. HererCOs 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, thatrCOs *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`.

    I said rCLneed not correspondrCY, not rCLdoes not correspondrCY. Just because you can write structures statically in PostScript doesnrCOt mean you
    have to.

    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 rCLeexecrCY operator, and we used this to discover
    that there was this other thing called rCLcexecrCY (only allowed within
    encrypted rCLeexecrCY 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, thatrCOs amazing! I only ever knew that there was some
    controversy about extracting fonts. Maybe this is how it was done?

    There are PostScript functions called rCLcharpathrCY and rCLpathforallrCY. The former lets you add a piece of text as outlines to the current path,
    instead of directly rendering it. Then you can create special effects
    with the usual filling and stroking operators. rCLpathforallrCY is a way
    to traverse the current elements of the path, invoking suitable
    callbacks for each. In PostScript level 1, the path was locked against traversal if it contained any outlines generated from AdoberCOs
    proprietary Type 1 fonts.

    This restriction was (mostly) lifted in PostScript level 2.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From albert@albert@spenarnc.xs4all.nl to comp.lang.forth,comp.lang.postscript on Thu Sep 10 12:22:38 2026
    From Newsgroup: comp.lang.forth

    In article <117sbbg$3qin1$2@paganini.bofh.team>,
    Waldek Hebisch <antispam@fricas.org> wrote:
    In comp.lang.forth albert@spenarnc.xs4all.nl wrote:
    In article <117r5b2$u52n$5@dont-email.me>,
    Lawrence D|+Oliveiro <ldo@nz.invalid> wrote:
    <SNIP>
    That|o??s a limited form of read-only introspection. You couldn|o??t do >>>transformations on the AST or such, could you? For a start, that would >>>require dynamic memory allocation, which Forth doesn|o??t have.

    Infinite memory is as good as dynamic memory. I can expand my Forth
    to e.g. 128 Gbyte that is for all practical purposes infinite.

    Maybe it is enough for use with Forth. I have a computation that
    runs out of memory in IIRC 100 GB. I do not think giving it full
    128 GB would make a big difference. OTOH I am pretty sure that
    this computation is finite, I simply do not know how much it
    needs, maybe 256 GB, maybe terabyte.

    More important, if you do not reclaim memory you eventually will
    run out of it. And modern processors are fast, so it can happen
    faster than you suspect. Of course, this is matter of programming
    style. In Lisp program when I monitor memory use I see gigabytes
    allocated in seconds. Without GC after few minutes all 128 GB
    that I have would be gone.

    I plan to use dynamic memory, if and when it becomes a problem
    for me. It is Forth filosofy. If the optimiser doesn't get
    finished at all, this is a waste of time.


    --
    Waldek Hebisch
    --
    The Chinese government is satisfied with its military superiority over USA.
    The next 5 year plan has as primary goal to advance life expectancy
    over 80 years, like Western Europe.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From albert@albert@spenarnc.xs4all.nl to comp.lang.forth,comp.lang.postscript on Thu Sep 10 12:30:10 2026
    From Newsgroup: comp.lang.forth

    In article <117ski9$1gjra$2@dont-email.me>,
    Lawrence D Oliveiro <ldo@nz.invalid> wrote:
    On Wed, 09 Sep 2026 14:42:10 +0200, albert wrote:

    On Wed, 9 Sep 2026 08:28:18 -0000 (UTC), Lawrence DrCOOliveiro wrote:

    ThatrCOs a limited form of read-only introspection. You couldnrCOt do
    transformations on the AST or such, could you? For a start, that
    would require dynamic memory allocation, which Forth doesnrCOt have.

    Infinite memory is as good as dynamic memory. I can expand my Forth
    to e.g. 128 Gbyte that is for all practical purposes infinite. Then
    it is a small change to use ALLOCATE that is not in the kernel of my
    Forth, but it is a loadable extension.

    Interestingly enough, some Lisp machines behaved in a similar way.
    They lacked garbage collection. After memory became full, you took a
    snapshot of all the live objects in your machine state, and rebooted. >Reloading the snapshot left all the garbage behind, so you were able
    to resume work from there, until memory filled up again.

    Apparently you could typically go for longer than a day before filling
    all of memory. Not sure if this was a sign of how large memories had
    become by then, or how slow Lisp machines were ...

    Still, you get the point that thatrCOs a crude way of managing memory.
    It is not good for handling long-running tasks.

    Also on modern multitasking systems, you might need to be running
    other things simultaneously, besides your Forth or Lisp program. So
    having the one process hog all available resources is ... not very
    sociable.

    My context is an optimiser. At one point the result is an optimised
    program, that is run, and all resources used by the optimiser can be
    released. The optimiser is not run for an undefinite time.

    Although garbage collection is convenient, ALLOCATE plus
    explicit release will be feasible for an optimiser, as far as I can tell.

    Groetjes Albert
    --
    The Chinese government is satisfied with its military superiority over USA.
    The next 5 year plan has as primary goal to advance life expectancy
    over 80 years, like Western Europe.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Peter Flass@Peter@Iron-Spring.com to comp.lang.forth,comp.lang.postscript,alt.folklore.computers,comp.lang.lisp on Thu Sep 10 07:39:22 2026
    From Newsgroup: comp.lang.forth

    On 9/9/26 21:06, Kragen Javier Sitaker wrote:
    (Apologies if this sounds like AI slop; IrCOve been interacting with
    Claude Opus 5 a lot today, and although I didnrCOt use any LLM to write
    any of this, its voice may be leaking into mine.)

    You are being assimilated. Resistance is futile.
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Hans Bezemer@the.beez.speaks@gmail.com to comp.lang.forth,comp.lang.postscript,alt.folklore.computers,comp.lang.lisp on Thu Sep 10 17:58:32 2026
    From Newsgroup: comp.lang.forth

    On 09-09-2026 10:10, Lawrence DrCOOliveiro wrote:
    Forth is not strongly-typed. IrCOm not aware of any high-level dynamic language that is not strongly-typed. At a bare minimum, you need to
    know what value is a pointer and what isnrCOt, otherwise memory
    management is not going to be a happy business.

    If you created that pointer -- and then forgot it was a pointer -- man,
    I'd be visiting a doctor.. :-)

    Or at least learn to use another commenting style. Gee, I do OOP without
    any safety net -- and I'm still breathing.

    Hans Bezemer

    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From antispam@antispam@fricas.org (Waldek Hebisch) to comp.lang.forth on Tue Sep 15 16:04:00 2026
    From Newsgroup: comp.lang.forth

    In comp.lang.forth Kragen Javier Sitaker <kragen@canonical.org> wrote:
    Lawrence DrCOOliveiro <ldo@nz.invalid> writes:
    On Tue, 08 Sep 2026 22:38:58 -0300, Kragen Javier Sitaker wrote:
    <snip>
    2. A function type. rCLIn Lisp, functions are first class objects-- [...] <snip>
    But, look again at PGrCOs explanation of what he means by rCLa function typerCY:

    rCLIn Lisp, functions are first class objects-- theyrCOre 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.rCY

    The only one of these that plausibly relates to homoiconicity is rCLhave a literal representationrCY. 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 thatrCOs
    still a string representation! And *most* languages don't have the
    ability to print out, for example, struct literals.

    What makes a rCLstring literalrCY or rCLinteger literalrCY a *literal* is that
    you can put it in your source code without having to give it a name.

    I think that this argument about literals or homoiconicity is very
    shallow. When people talk about "first class function" they mean
    that one can easily create new functions at runtime. And in fact,
    they usualy means that language supports closures. Closures may
    look as rather restictive way of creating functions, but properly
    implemented they are rather cheap and allow many interesting uses.
    AFAICS interpretive languages frequently support 'eval' and general
    'eval' allow definiting new functions at runtime. This is rather
    heavy-weight way of creating new functions. But instead of saying
    that 'eval' is too heavy-weight theoreticians prefer to give
    abstract conditions which admit closures, but exclude 'eval'.
    But AFAICS this boils down to simple thing: there is no way to
    make 'eval' as efficient as closures. Add to that that eval
    essentially implies dynamic type checking (or untyped language),
    while closures can be statically type checked and you see that
    for purpose of creating new functions closures have huge advantage
    over 'eval'.

    In PG writing things are presented in specific way because of
    his agenda: he wants to show that Lisp is a "top point" of
    developement of programming languages. And while Lisp has
    a lot of merits, PG aims made him ignore aspects where
    other languages got ahead of Lisp or made different but equally
    good choice.

    So the most plausible gloss of PGrCOs rCLhas a literal representationrCY 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 itrCOs obviously true of PostScript, but
    not of C, Pascal, BASIC, etc.

    AFAICS "literal representation" really means that you do not need
    a name for the thing. For example in Common Lisp package must be
    created with a name, so in this sense there are no package literals
    in Common Lisp, while in most other cases one can easiliy create
    anonymous objects.

    In more restrictive meaning "literal" may mean special syntax.
    With such meaning

    (cons 1 nil)

    is not considered to be list literal. But in Lisp users may extend
    syntax so that special syntax expand to expression above and at
    runtime there is limited difference (Common Lisp disallows modification
    to lists defined using standard syntax for list constants, but allow modification for list defined as above). So I think that in PG
    writing lack of name is more important than special syntax. Also
    I think that PG valued convenience and compact source code, so
    for example

    procedure(x); x; endprocedure

    while satisfies formal criteria (creates new anonymous function),
    would be deemed too verbose by PG.
    --
    Waldek Hebisch
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Paul Rubin@no.email@nospam.invalid to comp.lang.forth on Tue Sep 15 09:24:51 2026
    From Newsgroup: comp.lang.forth

    antispam@fricas.org (Waldek Hebisch) writes:
    In PG writing things are presented in specific way because of his
    agenda: he wants to show that Lisp is a "top point" of developement of programming languages.

    I remember thinking Lisp really is what you call a top point. The math
    term for that sort of top point is "limit ordinal". So my thought about
    that Blub essay was that PG didn't seem to realize that once you reach a
    limit ordinal, you can keep right on going, and eventually reach larger
    limit ordinals that have much different properties than the first one
    you reached.
    --- Synchronet 3.22a-Linux NewsLink 1.2