Resize Windows with Applescript

So I downloaded it with youtube-dl (after more annoyances with MacPorts updates ) and a helper script ytplaylist:

youtube-dl -i --yes-playlist --restrict-filenames -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.

Mature Programming Environment

Updating software annoys me.

I like Janie's Red Queen metaphor, a constant chase that gets nowhere, but you have to keep up. Updating software is utterly pointless if the old stuff still works, but if you don't then everything breaks.

If you give a person a program, you will frustrate them for a day. If you teach a person to program, you will frustrate them for a lifetime!

I'd rather like to have one of Vernor Vinge's "mature programming environments", where programming isn't a matter of boiling the ocean by starting over, or fixing code to match new APIs, but finding the parts you need and gluing them together with new code in old, ancient, centuries or millennia-old APIs that Just Work. For quite a while, the NeXTstep/Mac environment was like that, 30-year-old NS programs worked fine. But now everything is broken again.

Chicken Soup

(a bunch of stuff in a pot)

REPL

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")
^D

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
print(f"{x},{y},{z}")

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.

Building a Binary with Chicken Scheme

So that was fucking fun. Seems Chicken's docs aren't correct on how to build with modules, because those were added after the dark-ages R5RS it was modelled on. Finally got this working:

src/somelib.scm:

(declare (unit somelib))
(module somelib
    (hello)

(import chicken scheme)

(define (hello) (display "Hello!\n"))
)

src/somemain.scm:

(import chicken scheme)

(cond-expand
    (compiling (declare (uses somelib)))
    (else (load "somelib.scm")))
(import somelib)

(hello)

Don't you just love that muffin-man-muffin-man repetitious bullshit? declare is for the compiler, load is for the interpreter, then import for both. You import builtins like chicken, but use libraries (aforementioned sdl) without an import. Bizarre and contradictory.

build.zsh:

#!/bin/zsh
EXE=something
# LIBS is space-delimited, must not include main script
LIBS="somelib.scm"
MAIN=somemain.scm

cd src
for SF in ${=LIBS}; do
    SNAME=`basename $SF .scm`
    if [[ "$SNAME" == "main" ]]; then
        continue
    fi
    csc -c -j $SNAME $SF -o $SNAME.o || exit 1
done
csc -c $MAIN -o `basename $MAIN .scm`.o || exit 1
csc *.o -o ../$EXE || exit 1
rm *.o *.import.scm
cd ..

echo "Built $EXE"
% ./build.zsh
Built something
% ./something
Hello!

Hello, working native binary! There's a bunch more stuff about making a deployable app, but I'll take this for now. My actual program can show a blank green graphics window!

More Fun and Swearing with Scheme

I have ideas for some little games, which aren't suitable as giant Electron-powered applications. My preference in language for this would be Objective-C, Scheme, Pascal, C if I absolutely had to. Obj-C's lack of portability rules it out for now, but if GNUstep gets their crap together it may go back in the running. Scheme looked promising, I've liked using Chez Scheme for some data processing.

So after 2 days of experimentation and abuse, and none of my tools working right in the process, I was unable to get thunderchez/sdl2 to link. I can explicitly load the dylib, and it doesn't find SDL_Init. I wrote a little C program to prove that SDL is reachable, and it is. Chez just won't load it.

Frustrated, I grabbed Chicken Scheme again, did a chicken-install -sudo sdl2, ran the sample code(!), and bam, it just worked. 15 minutes of effort at most, and I'm up and running.

Down side, Chicken compiles slow, and the interpreter is PAINFULLY slow; Chez "interprets" by compiling fast with low optimizations. And Chicken defaults to R5RS which is… 1990 called and wants me to watch MIT SICP lectures on VHS. It has some R7RS support, but I prefer the guarantees of portability, safety, and specified behavior in R6RS. Have to go looking thru SRFI docs to find any feature, it's not batteries-included. Oh well, I'll probably live just fine without ideological purity.

Programming is a Joy

"Programming is a joy. That's why people do it. No one should spend hours in front of a computer terminal out of some dreary sense of duty, or because they have some vague notion of becoming "computer literate". That's not the point. Programming ought to be fun—and if you're not having fun, you shouldn't waste your time."
—Michael Eisenberg, "Programming in Scheme" (1988)

Software Tools Quote

"Finally, it is a pleasure to acknowledge our debt to the Unix operating system, developed at Bell Labs by Ken Thompson and Dennis Ritchie. We wrote the text, tested the programs, and typeset the manuscript, all within Unix. Many of the tools we describe are based on Unix models. Most important, the ideas and philosophy are based on our experience as Unix users. Of all the operating systems we have used, Unix is the only one that has been a positive help in getting a job done instead of an obstacle to be overcome. The world-wide acceptance of Unix indicates that we are not the only ones who feel this way."
"Software Tools in Pascal", 1981, by Brian W. Kernighan, P.J. Plauger

As every neckbearded n-gate reader will now rush to well-actually at me, BWK's experience writing this book led to Why Pascal Is Not My Favorite Programming Language, but note this rant is about "standard" ANSI Pascal, not the somewhat improved P-Code Pascal of the '70s or the free-wheeling super-powered Turbo Pascal of the early '80s, and nothing like modern FreePascal. Standard Pascal was a deliberately simplified pedagogical language, not a systems programming language, which the later ones are.

Anyway, the book's interesting as a problem-solving exercise, but the Unix part amused me. And no, Linux is Not Unix. Buy a Mac or install BSD if you want UNIX®.

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?