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

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

@interface CLICalc : NSObject

@property (retain) NSMutableArray *stack;

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


and src/CLICalc.m:

// CLICalc.m

@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;


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


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.


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 = 
keyword-foreground = 
keyword-background = 
builtin-foreground = 
builtin-background = 
comment-foreground = 
comment-background = 
string-foreground = 
string-background = 
definition-foreground = 
definition-background = 
hilite-foreground = 
hilite-background = 
break-foreground = 
break-background = 
hit-foreground = 
hit-background = 
error-foreground = 
error-background = 
cursor-foreground = 
stdout-foreground = 
stdout-background = 
stderr-foreground = 
stderr-background = 
console-foreground = 
console-background = 
context-foreground = 
context-background = 

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


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

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)

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.

Chicken Soup

(a bunch of stuff in a pot)


The Chicken csi REPL is appalling after using some nice REPLs, it doesn't even have history by default. I couldn't reliably get non-GNU readline-likes to work, so:

% chicken-install -sudo readline
% cat >~/.csirc
(use readline)
(current-input-port (make-readline-port))
(install-history-file #f "/.csi_history")

So at least now it has the usual up/down/emacs-like keys.

Long fucking ways from the old Symbolics LISP Machines. Why don't we have environments like that anymore? Why is everyone content to just use fucking emacs (I've never been emacsulated) or other editor, and a boring REPL? DrRacket is just a REPL that destroys its memory every time you edit code, and it's the most graphically advanced LISP-type environment. And this is why I still just use Atom with Symbols Tree View (even though it thinks variable definitions are functions), and copy-paste into iTerm if I want to test something.

Value Unpacking

Not having nice R6RS macros for this, and unwilling to fight with classic macros, I've been using values to unpack lists into variables, and because I can never remember the exact syntax, I made this cheat-sheet:

(define a '(1 2 3))
(define b '(4 5 6))
;; then one of these:
(define-values (x y z) (apply values a)) (printf "~s,~s,~s\n" x y z)
(set!-values (x y z) (apply values b)) (printf "~s,~s,~s\n" x y z)
(let-values [[(x y z) (apply values a)] [(q r s) (apply values b)]] (printf "qrs:~s,~s,~s xyz:~s,~s,~s\n" q r s x y z))

Probably not efficient, but better than car, cadr, caddr, etc. Maybe I should move all my list-structures into vectors, but then I'd still have to convert them to lists half the time. Here's where Python is the programmer's best friend, even if it is 10,000x slower:

a = (1, 2, 3)
x, y, z = a

Why Did LISP Fail?

How did a more advanced language with better tools just die off commercially, and now if you want to work in it, you have to cobble together a bunch of half-broken shit?

I think there's 3 reasons:

  1. It's hard and ugly. It may be logically compelling, but when you see a page of parens your brain panics and looks for a place to hide.
  2. Companies value the fake productivity of thousands of lines of C, Java, or Swift (aka C++2020) code more than having safety, security, and correct reasoning. Who cares if millions of people will suffer and possibly die from your code, as long as you can ship TODAY?
  3. A lot of LISP "hackers" are insufferable douchebags, both old beardy fuckers who've been doing it for 50 years and mewling children who learned it last week. Every new variant makes the older contingent more angry at even seeing a mention of it, and the sneering fetuses think whatever variant they learned is Divine Wisdom, rather than just an engineering tool that may need to be improved.

Text Filter

  1. I need a utility to filter some text, like awk but more modern regexp.
  2. Start writing utility.
  3. Discover I wrote this exact utility in 2006, and forgot about it. Apparently I did something with it in 2009. I may have blogged about it on my old site, too lazy to go searching.
  4. Clean it up for Python 3.7 and release.

Utility/Filter 1.1 can be downloaded, put it in your ~/bin or /usr/local/bin folder and call it with Filter.py -h.

BSD license, so do what thou wilt with it, but don't be a dick (uploading my stuff to github as if it's yours is dickish), OK?

Xcode Packaging

Speaking of packaging, a thing I hated in Xcode, which made me wish to be filled with nuclear fire, transformed into Godzilla, and stomp thru 1 Infinite Loop, was the package structure:

        all source, images, and config files in a single giant mess, regardless of "groups"
                    foo debug.xcscheme
                    foo release.xcscheme
        more sources

Are you fucking kidding me?

Groups aren't folders, they're just keywords grouped together until Xcode decides to fuck you over and disorganize them. To fight the project dump dir, I'd make a group, then add a subfolder, edit the group to reference the folder, and then I could create files in their own nicely-organized subfolder. There is of course no automatic name sorting, because that's a developer convenience and Xcode hates developers.

Test code couldn't be in the same folder as the code it was testing. So you'd edit a file, then edit a test file WAY over in a different tree. Good luck knowing how much coverage you had.

I especially love how foo.xcodeproj/xcuserdata/ and foo.xcodeproj/project.xcworkspace/xcuserdata/ have duplicate structures for develop & run/debug modes, because obviously the runtime and debug teams are separate and hate each other.

Icon Composer

In their ongoing efforts to break Mac development tools, Apple disabled and destroyed the rather nice Icon Composer.app because there's nobody left in-house who could figure out how to generate @2x images (protip: you use double size and rename it!), and now you have to use command-line iconutil with a set of magic filenames and no help.

So I wrote a little shell util, icontool.zsh:

if [[ $# -ne 2 ]]; then
    echo "Usage: icontool INIMAGE.png OUTFILE.icns"
    exit 1
rm -rf "$WORKDIR"
mkdir "$WORKDIR"
sips "$1" --resampleHeightWidth 1024 1024 --out "$WORKDIR/icon_512x512@2x.png"
sips "$1" --resampleHeightWidth 512 512 --out "$WORKDIR/icon_512x512.png"
cp "$WORKDIR/icon_512x512.png" "$WORKDIR/icon_256x256@2x.png"
sips "$1" --resampleHeightWidth 256 256 --out "$WORKDIR/icon_256x256.png"
cp "$WORKDIR/icon_256x256.png" "$WORKDIR/icon_128x128@2x.png"
sips "$1" --resampleHeightWidth 128 128 --out "$WORKDIR/icon_128x128.png"
sips "$1" --resampleHeightWidth 64 64 --out "$WORKDIR/icon_32x32@2x.png"
sips "$1" --resampleHeightWidth 32 32 --out "$WORKDIR/icon_32x32.png"
cp "$WORKDIR/icon_32x32.png" "$WORKDIR/icon_16x16@2x.png"
sips "$1" --resampleHeightWidth 16 16 --out "$WORKDIR/icon_16x16.png"
iconutil --convert icns "$WORKDIR" --output "$2"

Preferably feed it a 1024x1024 input image, it'll resize all the others; the small sizes might be blurry but it's good enough for most uses, and you can edit the contents of WORKDIR and run the iconutil line again if you need to.

Marzipan and Electron

Chris is missing the point of both technologies. And I'm sure not a brat Millennial.

Marzipan (candy frosting) is a legacy porting technology: Existing iOS apps can cost more to port to AppKit than they're worth, but may be worth something as a cheap Marzipan port. Nobody ports their iOS apps to tvOS or watchOS because it's not profitable, and everyone (in the first world with money) has an iPhone already.

I loved my UNIX® workstation Macs after suffering with Linux for a decade+, but Timmy Cook's Apple abandoned the Mac after Steve's death and Scott Forstall's firing. Anyone making new native Mac apps is in an abusive relationship: Apple does not love you, and does not care about the Mac.

I'd rather eat broken glass than run Linux again, and I have never and will never be a Windows weenie, but I'm not relying on Apple to support desktop developers ever again.

Apple's Mac apps have generally been shit for years now, because they won't spend the resources to develop & support their own stuff. iTunes is a bloated pile of crap, half-broken because it has to run on Windows, too; you don't like hybrid web apps? Everything except your library is web or XML rendering. Pages and Numbers were fast, minimally useful apps that got rewritten based on the iOS versions, and are just about useful for a memo or a chart, but not real work. Mail's a clusterfuck and not half as useful as when it supported more scripting and addons.

The new Mac commercials, first in years, show the broken-keyboard laptops and models they no longer make, nobody coding Mac apps, no desktop Macs. Where's that shiny "new" iMac Pro from last winter? Isn't that what a real musician would use? A near-blind photographer squints at a tiny Mac laptop instead of a giant 27" retina display?

This is how technical, developer-oriented Apple ads were in 2002:

Electron and Node are the future (along with Elixir, Go, Rust, maybe others?). It's 100x faster and more fun (attach the FunMeter™ somewhere fun) to code in than Swift, you can use any good editor instead of fucking Xcode, do layout with HTML/CSS instead of the rotting corpse of Interface Builder trapped inside Xcode. And it's cross-platform; 95% of the users (even in the first world with money) don't run Mac, because Apple never updates the Macs, they failed utterly to follow-through with Macs behind iPhones. There's 20x bigger market potential.

The future is certainly not banging your head against the Swift and Xcode walls, just to make a pure Mac app nobody will see. You can't make fun of Electron's runtime, which needs Node and Chromium, if you use Swift, which has a giant runtime turd because their amateur hour C++ compiler nerds can't make a stable ABI. The Mac's only future source of native apps is Marzipan ports.

There can be performance problems in Electron, but Slack's an outlier at 196MB binary and devouring 1.2GB RAM(!!!); it's the bloated WalMart-shopping fat-ass of Electron apps. Discord is also Electron, it has a 136MB binary and uses 360MB RAM, and does more, faster and better than Slack. Atom is the original Electron, has a 541MB binary, and uses 600MB RAM, for an entire editor/IDE.

My game currently has a 139MB binary, and uses 200-300MB RAM when running. Comparing to a random casual game from my Steam library, Chainsaw Warrior (well, "casual"; I've only beaten it once on Easy). It's based on Unity (another VM!), has a 249MB binary, and uses 200MB RAM when running, plus Steam itself uses 130MB RAM (I may yet integrate Steam into mine, so that may even out). It doesn't seem excessive.

I can't compare my Swift game prototype from 3 years ago, because it was written in a version of Swift that doesn't compile in any Xcode that runs on current hardware & OS, and Xcode "helpfully" deleted the built binary; who needs working binaries, right? I might have an old Xcode on my old laptop? Maybe I could waste a couple days fixing the code by hand in current Xcode, if I hated myself or loved Timmy Cook's Apple that much?

New languages evolve fast, but I can run 20-year-old Javascript and it'll run thousands of times faster than it did in the '90s, because the language was improved with forwards-compatibility in mind, hardware caught up, and the newer VMs compile & run it faster. I can compile 30-year-old Objective-C, and it'll run.

We had something similar to web tech 20 years ago with desktop Java, but the convicted criminal organization Microsoft sabotaged it and made a shitty single-platform ripoff called C#. Viruses became a problem for applets, which had nothing to do with desktop Java, but killed Java deployment even before Oracle bought & ruined SUN. Android runs on another Java ripoff, but their dev tools and APIs are even shittier than Xcode or C#, and the users are poor, so why make anything for them? Server-side development in Java, Clojure, or Scala, running on the Java VM, is hidden away in a back room, and made as boring as possible.

So now we have to reinvent the runtime, this time with Node & Chromium. OK with me.