Runes

A text filter to convert ASCII sequences into nice Unicode or emoji. Call it from your favorite editor, or on the command line:

% echo "BEFORE {circle:this is some hollow text.} AFTER" |runes.py
BEFORE ⓉⒽⒾⓈ ⒾⓈ ⓈⓄⓂⒺ ⒽⓄⓁⓁⓄⓌ ⓉⒺⓍⓉ⊙ AFTER

More instructions in the README file.

Spread of Terrible Programming Languages

Abstract—The English-like business programming language COBOL saw widespread use from its introduction in 1960 well into the 1980s, despite being disdained by computer science academics. This article traces out decisions made during COBOL’s development, and argues that its English-like appearance was a rhetorical move designed to make the concept of code itself more legible to non-programming management at computer-using companies.

I found some of the references much more interesting than the paper, which is a pretty high-level history avoiding the actual boots on the ground details.

COBOL was designed (and fought over very hard on this point) so that unskilled managers could "read" it, but in my view that had little to do with its spread. Middle management where that would matter has no buying power, and executives won't read more than a sentence on a slideshow.

Ubiquity made much more of a difference; no two computer installations were compatible until the late '60s, so the alternatives were COBOL, FORTRAN, LISP, and a hundred weird languages invented at each facility. Given those choices, I'd pick FORTRAN or LISP, but even COBOL would beat rewriting on every machine. A bunch of companies and government agencies ended up clustered on that choice, so it became widespread, not on any merits but because programmers could move code semi-automatically.

I know this because it happened at least five more times that I can think of, and only once with unskilled readability as a goal:

  1. BASIC is a tutorial language for children, very poor for large programs, very slow compared to C or ASM, grossly inferior to Pascal or Logo for any role. BASIC became ubiquitous because it can be implemented in a few K of RAM and worked nearly the same on hundreds of incompatible timesharing and microcomputer systems.
  2. Java is a mediocre Objective-C/Smalltalk replacement, applets turned out to be too heavyweight for the web and insecure, but cross-platform on servers turned out to be very valuable; cross-compiling C++ is a total crapshoot. Developers can have nice Macs and still compile Java code that runs on non-Mac servers.
  3. Linux (not a language, I know, but same pattern) is hot garbage, the product of a drunk, belligerent Finn student putting a kernel that'd get him a failing grade in an OS class on his 386. But because it's so quarter-assed and has no device driver support, it runs on anything like a virus. So now UNIX is all but dead, killed by a nematode parasite that fills the niche.
  4. PHP is a cruel joke, a gross hack to put server-side script in HTML instead of generating HTML in code or templating. But it was easily installed in Apache, runs everywhere with no setup. So half the web runs on this shit, from WordPress to Facebook.
  5. JavaScript started life as a six week hack to get LISP & Self-like programming, with C-like syntax for marketing reasons, in a web browser. And until early 2000s, it wasn't portable enough for anything useful. But when IE died and the other browsers implemented ECMAScript consistently, it became the universal language. It's still weird and fragile; I don't dare write it without eslint. But it may be the language of the century.

There's the similar case of IBM PC/DOS/Windows vs microcomputers and Macintosh, which were better tools but fragmented, but that's more about central authorities imposing Nazi-supporting IBM, and convicted criminal organization Microsoft bribing and extorting to kill competition. Common languages would likely have been enough to keep competition and diversity going if IBM & MS had been burned to the ground and their scatterlings shot as they ran back in the '70s.

The author of the paper sort of slouches in this direction but doesn't quite get it, when pointing out how science and technical culture has standardized on English. We are all incompatible machines, but a common language lets us argue.

I hate when papers list references without URLs:

  1. 10 PRINT CHR$(205.5+RND(1)):GOTO 10: Fun little book, not at all relevant to the paper.
  2. N. Wardrip-Fruin, Expressive Processing
  3. M.C. Marino, Critical Code Studies
  4. B. Schneiderman, The Relationship Between Cobol And Computer Science
  5. J. McCarthy, "Memo To P. M. Morse: A Proposal For A Compiler" Memo CC-56
  6. D. Nofre , M. Priestley , and G. Alberts, "When Technology Became Language: The Origins Of The Linguistic Conception Of Computer Programming, 1950–1960"
  7. M.D. Gordin , Scientific Babel: How Science Was Done Before And After Global English

Debugging in C

I've spent a horribly long time tonight staring at nested stack manipulation code now to get algebraic expression parsing (mostly?) working. I hate unary minus; life is pretty good except for that ugly little weiner with its binary operator twin, and then BAM weird compromises in your code.

For most errors, I rely on testing (even if just firing a test script through the language, as I'm doing with tbasic) and debug mode with verbose stderr logging. But this is C, where the slightest mistake can be EXC_BAD_ACCESS with no clue where. So then I need a debugger…

% make && lldb -o run -- tbasic -d test1.bas

[update: Forgot the -- before the program, which prevents lldb from reading those params. Command lines without parens are easy to get wrong!]

I don't really do much serious with lldb, I just need to see where an error occurred, backtrace (bt), and sometimes print some variables, to usually be able to solve a crash. It's a little frustrating that the lldb environment is so primitive, though, doesn't even have stdout, stderr (weirdly \<stdio.h> is callable), so how do I call utility functions? Had to rewrite some functions to take default NULL values.

Anyway, let and print (and error, my idiosyncratic stderr print) work, there's not a lot left in BASIC then I can get back to more serious things.

Programming in C

On tbasic, I've been doing all my C coding in BBEdit, not fucking Xcode, and it is fantastic. Doesn't crash. Syntax highlighting works, and by "works" I mean doesn't replace my text with Cyrillic as Xcode is wont to do (I do not like the new BBEdit color theme editor, but it's a far cry from stabbing me in the face like Xcode does). BBedit's window stays where I fucking put it, and sidebar shows clearly which files are open and modified. Running make from iTerm2 works fine, if you aren't an idiot and each compile produces less than a handful of errors. I can't really use BBEdit for JavaScript which needs more tool support, but for simpler languages, it's fine.

OS X Mojave no longer has C man pages visible anywhere I can find, so Dash is the only way to look anything up:

To use it from the shell, create dashman: (hashtag command-line integration, I couldn't find this in any search, and Dash has no AppleScript which is my usual solution to o'erweening GUIs)

#!/bin/zsh
open "dash://$*"

Hm. So, I've worked with people who don't learn their languages, they just rely on autocomplete in an IDE, snippets, and StackOverflow. If this is you, if you can't code without an Internet connection, you can't code. Please stop programming, go away, and read a book until you know the syntax and fundamental APIs, because right now you do more harm than good.

That said, while I studied K&R (and Stephen Kochan's Programming in C, my introduction back in the '80s) with the intensity of a snake-handler reading his Bible, I certainly can't remember every strcspn, strcoll, strstrn or whatever random series of 7-letter identifiers they had to use back in the '70s (even in the late '80s, I was still using C compilers which only distinguished 7-letter identifiers). C's libraries are often gibberish and searchable man pages are all we have.

The State of Software

On the horrible state of software:

Me Wearing a Scruffy, Profane T-Shirt: "Yeah, man, we should just code in bare metal like back in the '70s! Programmers should control machines, not the other way around! Liberation now!"

On shiny new things:

Me Wearing a Button-Up Dress Shirt: "Superb. Slightly more secure sandboxes in my giant JavaScript application service running on a giant pile of API stacks. I'll upgrade ASAP, I'm sure it won't destroy everything it touches."

Programming will remain very difficult

As an aside I would like to insert a warning to those who identify the difficulty of the programming task with the struggle against the inadequacies of our current tools, because they might conclude that, once our tools will be much more adequate, programming will no longer be a problem. Programming will remain very difficult, because once we have freed ourselves from the circumstantial cumbersomeness, we will find ourselves free to tackle the problems that are now well beyond our programming capacity.
EWD340, The Humble Programmer, by Edsger W. Dijkstra, 1972

Resize Windows with Applescript

So I downloaded it with youtube-dl (after more annoyances with MacPorts updates ) and a helper script ytplaylist: [updated 2019-06-22]

youtube-dl -i --yes-playlist --restrict-filenames --recode-video mp4 -o '%(playlist)s/%(playlist_index)s-%(title)s.%(ext)s' "$1"
osascript -e 'display notification "Youtube playlist downloaded"'

where $1 is the actual playlist URL; "show video list" under the video player or pick from DNA Lounge playlists

Now I have a folder full of properly-named videos. VLC can be opened from the shell with:

~/Applications/VLC.app/Contents/MacOS/VLC jwz_mixtape_200 &

Frustrated by VLC constantly resizing, I then ignored the problem for most of the morning, finally wrote resizeWindow.applescript:

#!/usr/bin/osascript

global appName
global windowX, windowY, windowW, windowH

on run argv
    parseArgs(argv)
    wrapCoords()
    resizeWindow()
end run

on parseArgs(argv)
    set argc to (count of argv)
    if argc ≠ 5 then
        display dialog "Usage: resizeWindow.applescript APPNAME X Y W H"
        error number -128 -- User canceled
    end if
    set appName to item 1 of argv
    set windowX to item 2 of argv as number
    set windowY to item 3 of argv as number
    set windowW to item 4 of argv as number
    set windowH to item 5 of argv as number
end parseArgs

-- Wrap negative coords around to other side
on wrapCoords()
    tell application "Finder"
        set desktopBounds to bounds of window of desktop
    end tell
    if windowX ≥ 0 then
        -- no changes
    else
        set windowX to windowX + (item 3 of desktopBounds) - windowW
    end if
    if windowY ≥ 0 then
        set windowY to windowY + 24 -- menu bar
    else
        set windowY to windowY + (item 4 of desktopBounds) - windowH
    end if
end wrapCoords

on resizeWindow()
    tell application "System Events"
        tell process appName
            set frontWindow to the first window
            set appPos to position of frontWindow
            set appSize to size of frontWindow
            -- display dialog ("front window of " & appName & ": " & (item 1 of appPos) & ", " & (item 2 of appPos) & ", " & (item 1 of appSize) & ", " & (item 2 of appSize))
            -- display dialog (appName & " at " & windowX & ", " & windowY & ", " & windowW & ", " & windowH)
            set size of frontWindow to {windowW, windowH}
            set position of frontWindow to {windowX, windowY}
        end tell
    end tell
end resizeWindow

Now I can just leave it running to update every 5 seconds:

while true; do resizeWindow.applescript VLC 0 -64 720 640; sleep 5; done

Slight annoyance, sometimes it's still expanding the size further down than it should until I size it smaller, and then it works. Fucking software.

I don't know that what I've done is productive in any way, but I have my MTV.

The kids are disco-dancing
They're tired of rock and roll
I try to tell them, "Hey, that drum machine ain't got no soul"
But they don't want to listen, no
They think they've heard it all
They trade those guitars in for drum machines and disco balls
We can't rewind now; we've gone too far
Internet killed the video star
—The Limousines, "Internet Killed the Video Star"

Advent of Code 2018

I don't know how much I'll do this year, but I'll do it in Chicken Scheme as a way of improving the text-processing and math functions in my library, and publish it. Source is now on gitlab, above.

The competitive part is still bullshit, the single starting time of midnight EST is utterly useless to most people who could participate; even in Pacific time, that's late at night. On my quiet night schedule, that's way too early to wake up; in Europe, that's 04:00 or so, long before coffee could percolate. Mid-workday for Korea & Japan. So, only for Finns and Russians?

Inline Documentation, or Lack Thereof in Scheme

I'm a big fan of inline documentation and "light" versions of Literate Programming, because docs that are more than one screen away from code are always wrong. That this is ever a revelation to anyone suggests to me that they've never written code or read API docs.

When I write Python, I write:

>>> import math
>>> def foo(x):
    "Square root of `x`"
    return math.sqrt(x)

>>> foo(5)
2.23606797749979
>>> help(foo)
Help on function foo in module __main__:
foo(x)
    Square root of `x`

Similarly in Java with Javadoc, I write:

/** Square root of {@code x} */
double foo(double x) {
    return Math.sqrt(x);
}

Javadoc isn't usable on live code or in a REPL (Java doesn't really have one), but you get nice HTML docs out of it. Javascript & Node don't have an official tool, but most code is marked up with Javadoc.

Common LISP, archaic pain in the ass though it is, has:

(defun foo (x)
    "Square root of `x`"
    (sqrt x))
> (documentation 'foo 'function)
"Square root of `x`"

Sadly and typically, the Scheme situation is much less organized.

There's a Chicken 4 egg hahn which is ugly, @() special forms and all the nested structures instead of just a string, but it's workable. Otherwise, everything seems to be external docs.

Racket has Scribble with a teeny-tiny side-note that you can put your docs in code, but no examples. There is a literate programming tool as well, but that's not quite what I'm after.

Chez Scheme has no solution, which is a little surprising given the "batteries included" philosophy.

Well, maybe I can get away with doing CLISP-type docstrings and worry about making a tool later? Does this extra junk hurt performance?

(import (chicken time))
(define (sqrt-without-docs x) (sqrt x))
(define (sqrt-with-docs x) "docs" (sqrt x))
(display "without docs\n")
(time {do [(i 0 (add1 i))] [(>= i 1000000)] (sqrt-without-docs i)})
(display "with docs\n")
(time {do [(i 0 (add1 i))] [(>= i 1000000)] (sqrt-with-docs i)})

In the interpreter, there's a 10-50% speed penalty (csi -s or in the REPL) for having that extra string created & GC'd, but compiled (csc), there's no noticeable difference. So I guess that's my solution for now.