Y C?

C has the great virtue that most computers have a C compiler installed or can get one from the vendor, and by typing:

% clang -o thing thing.c # or gcc if you must.

You get a binary that'll run on that platform. If you wrote your code sanely, you can recompile it on every major platform with no changes. If you find a place where a platform's different, you can add #if to fix that.

Contrast with every other systems language:

  • Do you have a compiler installed?
  • Is it portable?
  • Can you even fix non-portable areas?
  • Can you make a binary an end-user can run?
  • Can you call other libraries?

In C, the answer is always yes. In any other, it's "probably not, or only with great effort".

Someone was recommending Ada. It's not in standard gcc. You have to install something called "GNAT":

% sudo port install gnat-gcc
#  Ada is self hosted (http://en.wikipedia.org/wiki/Self-hosting)  #
#  You need to install an existing Ada compiler and then choose    #
#  an appropiate variant. For more info use:                       #
#  port variants gnat-gcc                                          #
% sudo port variants gnat-gcc
gnat-gcc has the variants:
   ada: Uses the MacPorts Ada (https://www.macports.org/) compiler to bootstrap!
   gnatgpl: Uses GNAT/GPL compiler (http://libre.adacore.com) to bootstrap!
   gnuada: Uses the GnuAda (http://gnuada.sourceforge.net/) compiler to
           bootstrap!
   macada: Uses MacAda compiler (http://www.macada.org) to bootstrap!
   odcctools: Use the odcctools instead of the system provided ones - does not
              work for x64 currently!

I dunno what to do here. MacAda hasn't been updated since OS X 10.4, which was 2005. I don't see any other ada in MacPorts. At this point I gave up and uninstalled everything. It's not a working language.

Go is fine except that it's a statically-compiled Java owned by Google, and every part of that is evil and wrong. The Rust Evangelism Task Force's constant whining has made me unwilling to even give all those &'s a chance, they're like digital Mormons. There's nothing really wrong with D Language except that it's not installed anywhere, and it's no safer than C for a lot of extra typing.

C isn't safe, but it's fast. It's pretty hard to use safely on large projects, but for small tools with static data buffers it's fine. You take some tradeoffs for any language.

For high-level programming, I love Scheme and JavaScript, and there's a ton of other good high-level languages. But if you need to write at the lowest level, and make it work everywhere, C's the only rational choice.

Fall of Visual Basic, Rise of QuickBasic?

There was simply no other tool that a developer could use to sketch out a complete user interface and get coding as quickly as VB.

Except Smalltalk on the Xerox Star (1981), ResEdit on Classic Mac (1984), RCS and ORCS on Atari ST (1985), Hypercard (1986), Interface Builder on NeXTstep (1988), and others; Visual Basic came out in 1991, Delphi came out in 1995. Maybe there was no other tool on brain-damaged DOS/Windows systems before VB, I can buy that, but real computers were doing RAD a long time ago.

Alan Cooper, the "father" of Visual Basic, has done some really interesting work with interaction design and user experience, but he didn't invent RAD. VB started life as his project "Tripod", a shell creation tool, basically a user-customizable program launcher or "wizard". Then Microsoft bought it and turned it into "Visual Basic", as a kind of ugly Hypercard (is BASIC a worse language than HyperTalk? Eh.)

VB became famous for a legendary feature called edit-and-continue, which allowed developers to run their programs, find problems, fix them, and then keep going with the new code. This was a sharp difference from almost every other programming environment known to humanity, which force developers to recompile their work and start over after every change.

This is, of course, utterly wrong. Every language with a REPL can do this: You write some code in the REPL, run it, it crashes or produces wrong results, you change the one offending function and resume. All global objects are still present, no recompile necessary.


#;2> (define (hello name) (error 'hello "Unimplemented")) #;3> (define (greetings) (for-each hello '(Cthulhu YogSothoth ShubNiggurath))) #;4> (greetings) Error: (hello) Unimplemented #;4> (define (hello name) (printf "Ia ia, ~A fhtagn!\n" name)) #;5> (greetings) Ia ia, Cthulhu fhtagn! Ia ia, YogSothoth fhtagn! Ia ia, ShubNiggurath fhtagn!

The one difference is that to save state, you have to copy-paste out of .csi_history or whatever. IDLE lets you save off your session history directly, but you still have to edit it into a working script. VB did have the virtue of staying in the live editor, but you're still just coding behind buttons, you have no access to a history or REPL.

(This is why I dislike DrRacket so much: If you edit code, it does destroy your global REPL state! Why even have a fake-REPL there, man?! Command-line Racket doesn't have this problem, but it's still not a great Scheme.)

Just as VB acquired the same power as C#, C# picked up the same conveniences as Visual Basic. For example, .NET’s type safety and memory management features meant that C# developers never needed to worry about memory leaks, just like VB developers.
In other words, C# now had the guardrails to protect hobbyists, students, and new programmers without surrendering its power. All of a sudden, VB was no longer something special. It was just another tool in a capable programmer’s toolkit.

Every part of that is… well, not correct.

  1. My understanding is VB.NET was a mess interacting with .NET resources, so if you used it you were still stuck in the VB gulag or had to learn so much C# or C you might as well move up.
  2. C# is a hard language to start up in, you would never give it to a newbie and say "good luck!"; although that's what Unity does, and most Unity code is nightmarish as a result.
  3. C# is certainly not "never need to worry about memory leaks"; it's a Java ripoff with more native libraries, many of which have dangerous C++ based memory management, and in any case you can over-retain things and fill up memory very quickly.
  4. No capable programmer is going to say "this project is best in… Visual Basic dot Net!" Except on broadcast TV-for-morons.

The one good thing from all this:

An innovative project called QB64 has created a modern QuickBASIC replica. It runs on Windows, MacOS, and Linux, with no emulator required.

OK, this is interesting. Terrible domain name www.portal.qb64.org though, what is happening there? Just qb64.org redirects to that mess.

Once you run the setup_osx.command, it has a qb64 binary, which opens a DOS-like window with the old DOS qbasic.exe UI. Huh. This isn't bad, though the font is small and the only way to change it is with a custom font. It ships with a cyberbit.ttf font which is pretty but very W I D E.

Writing something and hitting Run|Start dumps an untitled binary in the main directory. Saving (to the programs folder in this package) and then hitting the Run|Output EXE to Source Folder toggle does something more reasonable: hello.bas, hello, hello_start.command. A trivial program is 1186K, which is excessive, but it does work, opens a terminal and shows some text.

There's a ton of sample code in program/samples, including a lot of 3D stuff.

This is a pretty credible BASIC environment.

Pro:

  • Structured BASIC, if you want it.
  • You can just recompile on every platform.

Con:

  • No interactive REPL (that was one of the few things classic line-oriented BASIC, or hybrids like ST BASIC, had going for them).
  • Slow compile.
  • Ugly editor, though probably playing with fonts and colors would improve this.

You'll always be better off just giving a newbie Python, but some people may have old code or nostalgia.

  • Chipmunk BASIC is certainly nicer as an interactive environment, but probably hundreds of times slower.

Catalina Scripting

In particular:

Quartz Composer
Deprecations
Starting in macOS 10.15, the Quartz Composer framework is deprecated and remains present for compatibility purposes. Transition to frameworks such as Core Image, SceneKit, or Metal. (50911608)

Tragic but unsurprising. None of those are even remotely a replacement, being machine-level programming tools, not a graphical tool for assembling a graphics or sound workflow. But there's probably almost nobody using QC anymore, because Apple neglects it and won't promote any dev tools except horrible goddamned walking abomination Xcode.

Script Editor
Known Issues
Script Editor might quit unexpectedly when saving or executing scripts. (50470730)
Scripting Language Runtimes
Deprecations
Scripting language runtimes such as Python, Ruby, and Perl are included in macOS for compatibility with legacy software. Future versions of macOS won’t include scripting language runtimes by default, and might require you to install additional packages. If your software depends on scripting languages, it’s recommended that you bundle the runtime within the app. (49764202)
Use of Python 2.7 isn’t recommended as this version is included in macOS for compatibility with legacy software. Future versions of macOS won’t include Python 2.7. Instead, it’s recommended that you run python3 from within Terminal. (51097165)

More of the same, increasingly neglected non-Xcode tools.

Killing python2 is great. It's not clear if python3 will be included standard, or if you have to find that somewhere else.

On my old blog, I had a post "Macs Make Programmers", where I talked about all the great scripting languages and tools included in OS X, including Xcode back when it wasn't broken junk. Timmy Cook's Apple is making that very difficult.

So basically the first thing you need to do on a Mac is install MacPorts, sudo port install python3 and so on.


On the bright side:

This is a massive step up in security and usability. I was a long-time ksh user on HP-UX, Atari ST MiNT, and OS/2, switched to bash for Linux back in '95-ish, and went along with bash on OS X, even though the original default was tcsh. I switched to zsh in 2014, after the bash shellshock bug; and it was long overdue. Apple can't follow the current bash versions because they're under poisonous GPLv3, so even with shellshock patched it's still not safe. zsh is reasonably current and MIT-licensed so it can stay current.

You want one reason to switch right now?

F="foo bar"
rm $F

In bash, that removes two files, "foo" and "bar". In zsh, it removes one file, "foo bar" (you can get the Bash-like behavior of expanding args by rm $(echo $F), probably some other ways).

Terminal Condition

I spend half my time, easily, in a command-line terminal running zsh. So a new one, even one on an OS I don't run, is interesting:

There are some modern, nice conveniences in this. It's a little ways behind Mac Terminal.app (based on the NeXTstep Terminal from 1990), and vastly far behind iTerm2, but it's more advanced than the usual Linux terminals like rxvt, urxvt, or cross-platform Alacritty or Hyper.

Between this and WSL2 being a full Linux, it's plausible that the best Linux dev environment now (well, this summer when it's released) is Windows. The Year of the Linux Desktop is 2019, and it is owned by Microsoft®. Can you hear the tiny, distant screams of the FSF cultists?

Comparison based on code, reviews, and reddit thread with MS devs involved:

  • Scrollback: The single most important thing a terminal can do. MS does this, but doesn't have logging.

    Surprisingly, a lot of them only support a few pages. I keep mine at 10,000 lines or so, which is probably wasteful but so handy; I don't bother logging since my .zhistory keeps everything I typed, and I have Terminal.app and iTerm2 set to not close tabs automatically.

    Alacritty only just added scrollback last year.

  • Prompt Marking: Nope.

    This is a feature it's hard to live without once you've had it, no more paging up trying to see prompt lines (I have a red ANSI-colored prompt and it's still hard to see). In Terminal.app, Edit, Marks, Automatically Mark Prompt Lines, and then ⌘↑ and ⌘↓ move between them. iTerm2 has it enabled by default, and ⇑⌘↑ ⇑⌘↓ are the keys, which took me some re-learning.

    Nothing else has this, as far as I've seen.

  • Fonts: MS has programming ligatures and displays emoji, finally. Does not support RTL languages.

    I use Fira Code in all my editors and shells, and it's enormously helpful, more readable, and catches bugs: I look for === as a fat-equals symbol in JS, etc.

    Hyper, urxvt, Alacritty support Unicode fonts. rxvt stopped development almost 20 years ago so it barely shows 8-bit fonts correctly.

  • Tabs: MS has tabs! They're currently invisible until you add a second tab, same shit Terminal.app does, which annoys the hell out of me; I don't like UI that reshapes itself, reminds me of T-1000 Terminators (also makes it hard to tile my windows up correctly when they get resized).

    It's not clear if you can drag Windows Terminal tabs around to different windows.

    In iTerm2, I normally keep: First window with tabs for home shell, ssh into my server (running screen, so that has many open shells). Second window with 2 tabs for REPL, editor/running/compiling tasks, and sometimes a third tab for reading local docs. If I need more shells, I usually open them on the first window. I rarely open a third window for monitoring some long-running task; I just drag a tab out to its own window. All terminal windows are stacked on the left side of my screen, because there's no icons under that side of the Desktop.

    urxvt has tabs, but they're kind of a hack, not fully usable.

    Hyper has tabs, but they replace the title bar. Which is cool but also awful like a lot of things it does.

    rxvt and Alacritty don't do tabs, because they insist you use screen or tmux. Which sucks if you want to move a process from one window to another.

  • Profiles: MS supports multiple profiles, so you can use different ones for each task.

    So does Terminal.app, iTerm2, urxvt (but it's buried in a text file config).

    Alacritty, rxvt, and Hyper have a single profile and no UI for changing anything, hope you like editing text files and reloading.

    As far as I can tell, nothing else does automatic profile switching like iTerm2; when I cd to my ~/Code/CodeScheme folder, iTerm2 switches to my dark red transparent profile, including Scheme-specific triggers and copy/paste filtering.

    You can probably do that in urxvt's Perl(!) scripting, but it's not normal or easy.

  • Copy/Paste Filtering: Nope.

    iTerm2 and urxvt both let you set a bunch of regexp to run over lines to get selections correctly matching boundaries, not just space-delimited.

  • URL Highlighting: Nope.

    iTerm2, Hyper, and urxvt notice URLs and filenames, and let you click on them. In iTerm2, hold down ⌘ and click on any URL or path (like in an ls or find result!) and it does some useful action: Opens the URL in your browser or file path in your editor, by default, but you can configure that in the profile.

  • Custom Keybindings: Sorta? Doesn't seem complete, no idea if there's UI for it, but it does exist in their config.

    Most terminals can do this, but most can only remap a few actions. I like iTerm2's, as usual, which lets you bind any action, menu, or run a program on any keybinding. I mostly just use it to launch different profiles with starting paths & scripts.

    Terminal.app only lets you send specific text for a key.

  • Images: Sorta? Only if they're embedded in fonts.

    This is a neat trick in iTerm2: images. I use imgls all the time to see a thumbnail of every file with details (protip: I changed ls -ld in the script to ls -1Fskd for a more concise listing), and then ⌘-click to open what I want in Acorn; it's better than opening Finder and trying to read a long filename under a thumbnail.

    I'm unaware of anyone else being able to do this.

Tower of Babble

Programmers almost compulsively make new languages; within just a few years of there being computers, multiple competing languages appeared:

It proliferated from there into millions; probably half of all programmers with 10+ years of experience have written one or more.

I've written several, as scripting systems or toys. I really liked my Minimal script in Hephaestus 1.0, which was like BASIC+LISP, but implemented as it was in Java the performance was shitty and I had better options to replace it. My XML game schemas in GameScroll and Aiee! were half programmer humor, but very usable if you had a good XML editor. Multiple apps have shipped with my tiny lisp interpreter Aspic, despite the fruit company's ban on such things at the time. A Brainfuck/FORTH-like Stream, working-but-incomplete tbasic, and a couple PILOT variants (I think PILOT is hilariously on the border of "almost useful").

Almost every new language is invented as marketing bullshit based on a few Ur-languages:

  • C++: Swift
  • Java: Javascript (sorta), C#, Go
  • Awk: Perl, Python, PHP, Julia
  • C: Rust
  • Smalltalk: Objective-C
  • Prolog: Erlang, Elixir
  • ALGOL: C, Pascal, PL/1, Simula, Smalltalk, Java
  • LISP: Scheme, ML, Haskell, Clojure, Racket
  • BASIC: None, other than more dialects of BASIC.
  • FORTRAN: None in decades, but is the direct ancestor of ALGOL & BASIC.
  • COBOL: None in decades.

A few of these improve on their ancestors in some useful way, often performance is better, but most do nothing new; it's plausible that ALGOL 68 is a better language than any of its descendants, it just has mediocre compiler support these days.

Certainly I've made it clear I think Swift is a major regression, less capable, stable, fast, or even readable than C++, a feat I would've called impossible except as a practical joke a decade ago. When Marzipan comes out, I'll be able to rebuild all my 15 years of Objective-C code and it'll work on 2 platforms. The Swift 1.0 app I wrote and painfully ported to 2.0 is dead as a doornail, and current Swift apps will be uncompilable in 1-2 years; and be lost when Apple abandons Swift.

When I want to move my Scheme code to a new version or any other Scheme, it's pretty simple, I made only a handful of changes other than library importing from MIT Scheme to Chez to Chicken 4 to Chicken 5. When I tested it in Racket (which I won't be using) I had to make a handful of aliases. Probably even CLISP (which is the Swift of LISPs, except it fossilized in 1994) would be 20 or 30 aliases; their broken do iterator would be hard but the rest is just naming.

Javascript is a pernicious Herpes-virus-like infection of browsers and desktops, and nothing can ever kill it, so where it fits the problem, there's no reason not to use it. But there's a lot it doesn't do well.

I was leery of using FreePascal because it has a single implementation (technically Delphi still exists, but it's $X,000 per seat on Windows) and minimal libraries, and in fact when it broke on OS X Mojave, I was disappointed but I-told-you-so.

I'm not saying we should quit making new Brainfuck and LOLCODE things, I don't think it's possible for programmers to stop without radical brain surgery. But when you're evaluating a language for a real-world problem, try moving backwards until you find the oldest and most stable thing that works and will continue to work, not piling more crap into a rickety new framework.

The Biblical reference in the title amuses me, because we know now that it requires no malevolent genocidal war deity scared of us invading Heaven to magically confuse our languages and make us work at cross purposes; anyone who can write and think splinters their thought into a unique language and then argues about it.

Scheme Record to RecordType

So, in "classic" Scheme (up to R5RS), there were no structs/records/classes. You could fake them with a Vector and writing all the accessor methods by hand, but it sucked.

SRFI-9 added a very minimalist record type with a lot of repetition, and no inheritance, though at least SRFI-17 fixed setters.

R6RS (as implemented in Chez Scheme) added a much more powerful system with inheritance and constructor methods, but half the Scheme community hates nice things and voted it down. There's a half-assed reimplementation of R6RS in SRFI-99 and sequels, but it still doesn't have read/write mechanisms. R7RS still only ships with SRFI-9 built in. Unbelievable mess.

Chicken has a convenient define-record macro, and read/write methods, but by default uses SRFI-9, and hides SRFI-99 in an egg; so chicken-install -s srfi-99 and then (import srfi-99) everywhere, and then write a ton of boilerplate for every type. So I just automated it with Python (doing string parsing in Scheme is more annoying):

Documentation is in the module help (or just read the source, Luke). I use it by writing the Chicken macro (define-record Point x y), then at shell:

% pbpaste|schemeRecordToRecordType.py|pbcopy

And paste back:

;; (define-record Point x y)
(define-record-type Point  
    (x)
    (y)
)
(define-reader-ctor 'Point make-Point)
(define-record-printer (Point p out)
    (format out "#,(Point ~S ~S)"
        (Point-x p) (Point-y p) 
))
; module exports:
; make-Point Point? Point-x Point-y

Note that none of this really gets me an "object-oriented" system, but records are sufficient for most programs, and inheritance works with define-record-type. There are OOP systems but I don't especially like any of them so I'm passing on them for now.

Writing Objective-C with Mulle-Objc

mkdir CLICalc
cd CLICalc
mulle-sde init -m foundation/objc-developer executable

This takes more or less forever.

… Still going. OK, finally done. I hate to think it's gonna do that every new project? Or whenever it updates?

Anyway, bbedit . (fuck Xcode), and add at the bottom of import.h and import-private.h:

#import <Foundation/Foundation.h>

Make src/main.m useful:

// main.m
 "import-private.h"
 "CLICalc.h"

int main(int argc, char *argv[]) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    CLICalc *calc = [[CLICalc alloc] init];
    [calc push:42.0];
    [calc push:69.0];
    double a = [calc pop];
    double b = [calc pop];
    NSLog(@"a=%f, b=%f", a, b);

    [pool release];
    return 0;
}

Create an Objective-C class, src/CLICalc.h:

// CLICalc.h
 "import-private.h"

@interface CLICalc : NSObject

@property (retain) NSMutableArray *stack;

- (void)push:(double)n;
- (double)pop;

@end

and src/CLICalc.m:

// CLICalc.m
 "CLICalc.h"

@implementation CLICalc

@synthesize stack = _stack;

- (id)init {
    self = [super init];
    _stack = [[NSMutableArray alloc] init];
    return self;
}

- (void)dealloc {
    NSLog(@"CLICalc dealloc");
    [_stack release];
    [super dealloc];
}

- (void)push:(double)n {
    [_stack addObject:@(n)];
}

- (double)pop {
    if ( ! [_stack count]) {
        // ERROR: stack underflow
        return 0.0;
    }
    double n = [[_stack lastObject] doubleValue];
    [_stack removeLastObject];
    return n;
}

@end

Doing that without a template was a little hard on the old memory, and I had to use DDG to look up some method names without autocompletion. But I'm pretty sure that's fine.

In mulle-ide, type update to add the new class to cmake: If you look in cmake/_Sources.cmake you should now see CLICalc.m listed.

Now craft to compile. You'll get a spew of crap, but hopefully no errors.

I am getting this, which I can't resolve:

/Users/mdh/Code/CodeMac/CLICalc/src/main.m:21:55: warning: 'NSAutoreleasePool'
      may not respond to 'init'
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
                                   ~~~~~~~~~~~~~~~~~~~~~~~~~ ^
1 warning generated.

But NSAutoreleasePool certainly has init, and it seems to not die?

% ./build/Debug/CLICalc
a=69.000000, b=42.000000

Hooray!

Yeah, this isn't amazing. Except: It's supposedly portable now. I can maybe rebuild this on Linux, or Windows? I dunno.

This is almost classic Objective-C, slightly enhanced from 1.0: We didn't have property/synthesize, or nice object wrappers like @() when I were a lad. I typed so many [NSNumber numberWithInteger:n]. So get used to the retain/release/autorelease dance. There's no dot-syntax for property access, type them [] like old-school. But hey, it's a proper compiled language with a nice object system and no GC pausing.

I tried importing Cocoa and got a ludicrous spew of errors, so Mac GUI is gonna be a challenge. But I could import SDL and use that for portable UI, since Objective-C is just C.

Sweet. I'll finish up the calculator's parser in a bit, but then see about doing something useful in it.

Interactive Python IDLE

When using Python from a shell, the REPL is fairly awful and doesn't let you copy-paste or save, except through the shell itself. I especially find that copy-pasting functions in is error-prone. There's a nice interactive environment for Python called IDLE (after Eric ): It's probably an application in your /Applications/Python 3.7 folder, or whatever other OS's do, or can be run with IDLE3 from the shell. Other than using ^N/^P for next/previous history line, it works pretty much exactly as you'd expect a GUI REPL to work, and lets you save your session as a text file, easy to extract some functions from later or use as a doctest.

Trouble is, IDLE doesn't automatically pick up the ~/.pystartup script; I had to remember to call it with IDLE3 -s, and there's no easy way to do that from the desktop, where I'm often lazily clicking. This has been frustrating me very slightly for years.

So: open Automator, new Document, add Utilities/Run Shell Script, and paste in:

IDLE3 -s

Save as an Application, name it IdleStart. The icon's ugly and generic, so I made a half-assed icon, copy it from an image editor or Preview, and paste into the icon in Get Info on the application.

python-foot

Now I have a nice stupid foot to click on, and get a proper REPL. The running program has a hideous 16x16 or 32x32 icon scaled up, which I don't think I can easily solve; I looked at the idlelib source and while I could patch it, it's not easily configured. Maybe later. There's also no way to specify the window location, which I'd like to have mid-left of my screen, but again, maybe later.

While I'm at it, the themes are garish, and the customizer is unpleasant. So I just edited this in ~/.idlerc/config-highlight.cfg, then picked Mark's Dark theme:

[Mark's Dark]
normal-foreground = 
normal-background = #111111
keyword-foreground = 
keyword-background = #111111
builtin-foreground = 
builtin-background = #111111
comment-foreground = 
comment-background = #111111
string-foreground = 
string-background = #111111
definition-foreground = 
definition-background = #111111
hilite-foreground = 
hilite-background = #808080
break-foreground = 
break-background = #808000
hit-foreground = #002040
hit-background = 
error-foreground = 
error-background = 
cursor-foreground = 
stdout-foreground = 
stdout-background = #111111
stderr-foreground = 
stderr-background = #111111
console-foreground = 
console-background = #111111
context-foreground = 
context-background = #444444

My current ~/.pystartup is short (in python2 it was much longer, since nothing worked right), just some common imports and functions I use enough to miss:

import os, sys
import math
import random as R
import re
import time
import turtle as T

def dice(n, s):
    t = 0
    for i in range(n):
        t += R.randint(1, s)
    return t

def roundup(n):
    return math.floor(n+0.5)

def sign(i):
    if i > 0: return 1
    elif i == 0: return 0
    return -1

print("READY")

Now if I click the foot and see "READY" above >>>, it's all working.