Blog

Seventies Thursday Music

  • ★ '70s Greatest Hits ★: Very K-Tel, but a great collection from my youts. It's always bizarre to hear some of these without vinyl hiss or radio static.

I especially want to call out "Don't Fear the Reaper", "Dust in the Wind", and (not on this collection, but I heard it earlier tonight) "The Grand Illusion" by ELO, for making pop music from death and mortality, not just bullshit about love.

What I'm Watching: The I-Land E4-7

Despite my contempt for the writing, I finished this off. I'm going straight to spoilers here; at least watch E1-3 cold.

So E4-E6 goes back to the I-Land, and there's a terribly-written argument of "you have to believe my incredible story!" "no you're a liar!" "waah, you bitch!" for half an hour or more; I barely exaggerate, it's a tale told by an idiot. Some light bondage but it's not very hot.

A couple claiming to be "Bonnie & Clyde" show up and deliver more preposterous threats and tell everyone remembering their past will make them sad. Then everyone splits up, has flashbacks, and yes they all suck IRL as well as on I-Land. The past is a land of crying people and shitty Instagram filters, and repeated scenes because they didn't film enough to pad the episode out. Now, serious SF moment here: I don't think any kind of memory suppression is plausible, the brain doesn't work like a database, something this targeted and reversible especially not, and it's completely contrary to the reformation attempt. So the main plot point technology is just nonsense fantasy.

Chase continues to be the only interesting character, so Cooper runs off after her to stay relevant and on-camera. We do eventually learn a whole sad backstory, except: It is implausible they would both be incarcerated together, both taken in the program, and hook up again despite memory wipe. It feels tacked on, or like Lost the soi-disant writers were just making up bullshit as they went along.

Taylor sails away to II-Land (second island, ha ha), which has the single dumbest plot element in the show; never drink free chicken soup! Bonnie & Clyde showing up to be shitty Rod Serlings is just salt in the wound. Said shitty plot element is never seen; is it another player or an NPC or just B&C?

Nothing at all is resolved, until KC and her bozo follower confront Chase & Cooper, violence ensues, B&C show up, and there's quite a good fight scene again. The fight choreography is quite good, someone competent was running that. But also here KC and her bozo just vanish when no longer relevant to the plot.

And finally in E7, back in "reality" more or less, Chase gets released because everyone believes a single thing Cooper says. Except we have to sit thru more of the Warden being an idiot, the doctor & academics being patronizing, everyone getting some kind of comeuppance which is very implausible; especially putting anyone new into the simulation.

The final twist about Chase isn't really shocking, since she didn't recognize any advanced tech in E3, it had to be some time ahead, but it's too far: A 50-year-old woman who's been in a sensory deprivation tank for 25 years cannot do the fighting we saw in E3.

The casual "Galveston is flooded" thing is cute, but a real post-Global Warming Galveston isn't going to be a temperature-habitable zone either.

They seem to think she's doomed, being discharged with pocket money & bus fare; that's stupid, she can go to any tabloid successor of Buzzfeed and make millions on the story, and any yellow rag journalist can write it better than these writers did.

The entire series is so terribly written it may as well have been improvised by the actors, except they did all the flashbacks, so it's actually scripted this badly. The same premise, if handled by a competent writer, director, and hiring more than one competent actor, and having more fight scenes, could've been much better.

★★½☆☆

What I'm Watching: The I-Land

10 very pretty people wake up with amnesia on a beach, on a deserted tropical island. As they wander, they find useful items for survival. If they go in the water… da-dum da-dum… They have some stupid interpersonal drama.

Then the very telegraphed "twist" happens; which given the staticky intro effect, and the unreal nature of many events, should be no surprise at all.

Up thru E2 it's a mediocre Lost with really terrible dialogue and deliberately no character development. Then E3 is the exposition ep, but the writing is even worse, with a fat moron not answering questions, then terrible caricatures of academics not answering questions. The only saving grace is one good action scene. But these supposed future police have no idea how to handle prisoners, they're like the idiot cops in Demolition Man trying to handle Wesley Snipes, but this isn't intended as dark comedy.

★★½☆☆ as of E3 out of 7 — If I hadn't seen Lost, The Cell, or Demolition Man, I'd think this was at least sort of creative. Probably some people will call out The Matrix, but that's a happier kind of prison.

The thing of casting only pretty people for the island and often ugly ones for outside is a shallow trick, and I find it kind of insulting.

A Scheme of Gerbils Runnin' in a Wheel

Chicken's mediocre performance is causing me problems, so I'm taking yet another language tour/spike.

Looking at Gerbil Scheme, as a faster Scheme and as a bonus it's closer to R7RS. It has far less library support than Chicken, but does have a FFI, and compiles to a native binary through C, so I really don't need too many libraries.

Instead of the #!key #!optional #!rest keywords used in Chicken and some others, Gerbil uses a literal template style for functions:

;; Chicken: (define (foo a #!key (b 'bar) #!optional (c "see?") #!rest rest)
;; Gerbil:
(def (foo a b: (b 'bar) (c "see?") . rest)
    (print "a=" a " b=" b " c=" c " rest=" rest "\n")
)
(foo "Mark" 1 b: "bees!" 'what) ;; note out-of-order optional & keyword
;; a=Mark b=bees! c=1 rest=what

I like this style better, but I dislike the "def" keyword (which uses the enhanced lambda that does this) instead of "define" which uses "lambda%" (traditional lambda).

Gerbil uses [] for list construction, and {} for method dispatch, so goodbye to the nicely distinguishable braces I was using in Chicken. The spec says implementations can do what they want with those symbols, but I wish they wouldn't. Ah, well. I'll add a shitload more whitespace and some comments and it'll be fine.

The struct/object system in Gerbil is pretty nice, they're slightly upgraded records, but there's an (@ obj field) syntax instead of (MyClass-field obj), and (defmethod {mymethod MyClass} ...) creates a polymorphic {mymethod obj args} which finds the right method for any object, which is especially useful for inheritance.

I tried doing some VT100 graphics just to proof-of-concept, but it's doing something to the terminal, such that the same escape codes which work in Python don't clear the screen fully in Gerbil. After a short losing battle with stty and termcap, I give up on that and I'll jump right to writing a C FFI to SDL, because in 2019 that's easier than writing to a console.

Daily reminder that everything we have made since 1984 is overcomplicated junk. On an Atari 800, this took a few seconds to type, and you could start coding a nice UI instantly:

atari-is-awesome-graphics1

Alas, we live in a fallen world, so this is going to be trouble. Here's my Gerbil FFI template so far:

package: myffi

(import
    :std/foreign
)

(export #t)

(begin-ffi
    ;; names of all Scheme wrappers to expose
    (chello)

(c-declare #<<CDECLEND

#include <stdio.h>

int chello(void) {
    printf("Hello this is C!\n");
    return 1;
}

CDECLEND
)

    (define-c-lambda chello () int "chello")

) ;; begin-ffi


; TODO for constants: (define FOO ((c-lambda () int "___result = FOO;")))

(define (main . args)
    (chello)
)

Gerbil has a Scheme-based build system, but I'm a caveman so I make another build.zsh:

#!/bin/zsh

function usage {
    echo "Usage: build.zsh MAIN.scm [LIBS] || -?"
    exit 1
}

if [[ $# -eq 0 || "$1" == "-?" || "$1" == "--help" ]]; then
    usage
fi

mkdir -p bin

main=`basename $1 .scm`

gxc -exe -static -O -o bin/$main "$@" || exit 1
echo "Built bin/$main"

Now:

% ./build.zsh myffi.scm
Built bin/myffi
% bin/myffi
Hello this is C!

Hooray! Unconditional success! Only took all afternoon and a half-pot of coffee!

Now I "merely" have to wrap all of SDL (well, just the parts I need) and get linking working and oh sweet merciless Cthulhu. And I still won't know how much this'll help my performance until I'm days into it. But, the first step is the hardest.

Not-Atari Still Has No VCS

"Atari SA", formerly Infogrames who bought the name, are still not shipping their fantasy "modern VCS", to everyone's total lack of surprise.

This is just about physically painful as these useless parasites take peoples' money (not mine) for a grift, based on love of what a different company named Atari was like 30-50 years ago.

Now they're so shitty, their con so obvious, they've been removed from moderation of the subreddit about them.

Code Mystics' Atari Greatest Hits collection is unrelated, and a fantastic emulation on iOS and DS.

REXX Primes

Just a quick sanity check on performance.

/** primes.rexx */
/* Created 2019-09-26 */
/* Copyright © 2019 by Mark Damon Hughes. All Rights Reserved. */

PARSE ARG primeCount
IF \ DATATYPE(primeCount, "W") THEN DO
    SAY "Usage: primes.rexx N"
    EXIT 1
END

CALL clearPrimes
CALL sievePrimes
CALL printPrimes
EXIT 0

clearPrimes: PROCEDURE EXPOSE primes. primeCount
    primes. = 1
    primes.0 = primeCount
    primes.1 = 0
RETURN

sievePrimes: PROCEDURE EXPOSE primes. primeCount
    DO i = 2 TO primeCount
        DO j = (i * i) TO primeCount BY i
            primes.j = 0
        END
    END
RETURN

printPrimes: PROCEDURE EXPOSE primes. primeCount
    DO i = 1 TO primeCount
        IF primes.i THEN CALL CHAROUT , i || " "
    END
RETURN
# REXX: 0.8% C
% time rexx primes.rexx 1000000 >~/tmp/primes-rexx.txt
rexx primes.rexx 1000000 > ~/tmp/primes-rexx.txt  6.43s user 0.36s system 99% cpu 6.831 total

# Regina: 1.53% C
% time regina primes.rexx 1000000 >~/tmp/primes-regina.txt
regina primes.rexx 1000000 > ~/tmp/primes-regina.txt  3.25s user 0.26s system 99% cpu 3.521 total

# Python: 4.8% C
% time ./primes.py 1000000 >~/tmp/primes-python.txt
./primes.py 1000000 > ~/tmp/primes-python.txt  0.75s user 0.02s system 68% cpu 1.123 total

# Julia: 1.4% C
% time ./primes.jl 1000000 >~/tmp/primes-julia.txt
./primes.jl 1000000 > ~/tmp/primes-julia.txt  0.45s user 0.35s system 21% cpu 3.797 total

Most of REXX's bad time can be attributed to using stem variables in a tight loop, effectively string-keyed hashtables, so I'm sure an ooRexx Array implementation would be significantly faster. But stems are what you'd use in "real code", so caveat coder. In a long real-world program I don't think it's as big an issue, but it's definitely not received the kind of optimization love that newer languages have. Aesthetically, the REXX source is a little wordier and more explicit about globals access, but not hard to write or read.

I went ahead and grabbed Regina, and it doubles the speed of ooRexx. I'm still wary of it, but that's a big win.

Python's not terrible at anything; it's within a stone's throw of a compiled language at this point. Competent mediocrity has really made Python the new dynamic Java of our time. But you still can't do multithreading in it.

Julia's slow because the startup time is just atrocious; if I timed it internally after startup it'd be as fast as a compiled native program, but as a scripting language Julia's bad news.

(I do have real work to do, but I'll keep playing with REXX more over the next few days)

REXX Monarchial Madness is Hereditary

So, I was trying to figure out how to manage global vs. local state, and made the dumbest program possible:

/** array.rexx */

a. = 0
a.0 = 10
DO i = 1 TO a.0; a.i = i; END

SAY array_string("a.") -- prints 1 to 10
SAY alen -- prints "ALEN" because it doesn't exist in global scope

EXIT 0

/* Returns string representation of an array, stem. passed as first arg. */
array_string:
    PARSE ARG _arrayvar .
RETURN _array_string()

_array_string: PROCEDURE EXPOSE (_arrayvar)
    alen = VALUE(_arrayvar || 0)
    str = ""
    DO i = 1 TO alen
        IF i > 1 THEN str = str || ", "
        str = str || VALUE(_arrayvar || i)
    END
RETURN str

So… I've made a global variable "a." full of numbers. PROCEDURE makes a local variable context until you hit RETURN; you should never work at global scope if you can avoid it. So to pass a. in, I have to bounce through a global function array_string(), set a global var _arrayvar to whatever arg was given, which calls a local function _array_string and the paren in EXPOSE (_arrayvar) expands that into "a.", and then use VALUE() to dynamically read the variable.

Well, it works. I remember now how crazy complex REXX programs got, with giant global databases being exposed in every function. I bet this is the origin of my "World object" pattern in so many later languages.

This and another dumb example or two ate a couple hours of my brain, but it kinda makes sense again, you know? Interesting for a language I haven't written in anger in 20 years. I went fully back to SHOUTING KEYWORDS, it just makes sense.

The Python version of this is:

#!/usr/bin/env python3

def array_string(arrayvar):
    return ", ".join(map(str, arrayvar))

a = []
for i in range(1, 11): a.append(i)
print(array_string(a))

Which I'd argue is just as stupid, because Python thinks ranges end 1 before the last number, doesn't know how to join numbers with strings, on a large array that map will consume all memory, and is a single-pass interpreter so I have to put the function above the script code; but it is much, much shorter.

REXX is Still the King

Random REXX mention on Fediverse reminded me of good times with it. REXX was originally a scripting language for the IBM /360 series, later OS/2 where I encountered it. There's three main branches: REXX (original, simple procedural language), ooRexx (strict superset adding OOP tools to REXX), and NetRexx (almost-REXX implemented in Java, so it can interact with the entire Java API).

The things it does well are: Simple notation, like BASIC, but it's structured and dynamic: Variables have their NAME as default value, and it's trivial to evaluate strings as code. Variables can also be "stems", either indexed or associative arrays depending on key. a.=0; a.0=10 and you have an 10-element array of 0's (using .0 as length is common). Any command it doesn't recognize is passed to the outside environment, which might be a command-line shell, another program you're scripting, whatever, no special syntax needed. REXX is preposterously fast for a scripting language, and has perfect decimal math (Cowlishaw's main academic area of interest), though it's still not compiled language fast.

OS/2 made great use of it. The E and E/PM editors written in REXX are all about scripting; like Emacs but far more so, there's almost no program except the E script.

Back in the '90s, I wrote a ton of programs for clients (all dreadfully dull line-of-business stuff) which were thousands of lines of REXX with VT100 UI or a little teeny GUI shell in C it controlled and got messages from; my turnaround on a business change could be hours instead of days or weeks for a C-only program. There's somewhere on FTP sites or in my old code archive my QUEST game, which was just a random D&D wilderness crawl hack-and-slash, written the same way, and a huge script for writing a nice config file to generate the impossibly fussy bit-twiddling world files for DikuMUD and CircleMUD.

Down side, by the late '90s OS/2 was shut down, Mike Cowlishaw started doing everything in NetRexx, so the core language languished, and there weren't good ports to Linux or Mac (either System 9 or early OS X). The open-source "Regina" interpreter was almost but not quite compatible, and was very frustrating to debug around. I completely gave up on it around 1999, when Python got usable.

Seems in the last decade they've somewhat recovered, REXX Language Association, aka ooRexx.org, and NetRexx all have maintained sites, the old RexxInfo site is as hideous as it was in the '90s, and unmaintained now, but does have a free copy of the REXX Programmer's Reference book which is nicely complete.

The RexxLA symposium is going on as we speak. No livestreams, sadly, but last year's presentation decks are up. You might want to start with Rexx Tutorial for Beginners part 1, part 2, and ooRexx Tutorial.

I'd really like to hear "Rexx from OS/2 to macOS - a travel in time & space"!

% sudo port install oorexx
…
% cat hello.rexx
/** hello.rexx */

name = input("What is your name? ")
say "Hello," reverse(name) || "!"
exit

input: procedure
    parse arg prompt
    call charout ,prompt
    return linein()

% rexx hello.rexx
What is your name? Mark
Hello, kraM!

Works fine. The default I/O commands are a little weird so I rewrote that input function almost from muscle memory. PULL var and PUSH x operate on a stack and get uppercased, PARSE PULL var is very powerful and lets you interpolate strings for parsing input (you can also use it on function args, as seen here), but here all I want is a complete line as a function. SAY is a cutesy form of CALL LINEOUT ,whatever, (the missing first arg to CHAROUT or LINEOUT is the output file name/handle). Note the two ways to call a function: y=f(x) or CALL f x depending on if it has a return value. I write everything lowercase, modern REXX is case-insensitive, but historically it was an uppercase-only language, so I always SHOUT THE KEYWORDS when talking about them.

I didn't get far into ooRexx back in the day; I don't think most of its "enhancements" are needed to a nice simple language, but there's places where stems aren't a sufficient abstract data structure and the like.

This looks like fun to play with again, though I wouldn't expect to ship anything useful in it. The one problem is there's no REPL. You can actually make a simple REPL with just DO FOREVER; PARSE PULL line; INTERPRET line; END but there's a lot of error-handling and structured stuff that won't work in that. I think I have a real REPL in my code? I'll go looking in a bit.