Posts from July 2020

Swift TIL 12

2 min read
TIL

First a small aside: To be honest, the T part of TIL is getting to be less and less true with this series of posts, but the posts themselves serve a useful purpose for me. As I've been reading the book I'm working through, I've also been making notes on my iPad in the Apple Notes application. I'm not really convinced that that's the best final location for such notes, so early on I made an extra step in keeping track of what I'm doing and trying to reinforce what I'm learning: transfer the notes into Org-Mode documents in my notebook repository.

This repository contains lots of Org-Mode files, broken down into subject directories, that hang on to longer-form information I want to keep track of regarding software development and general operating system use. I'm sure you know the sort of thing, the things where you know you know them but you can't always retain all the detail -- so having the detail where you know you'll find it is useful.

So I've made that part of the process: read book, find thing worth noting, note in Apple Notes, a wee while later re-read and test out by writing sample code and transfer to the Org-Mode notebook. During that latter step I sometimes also write up something that I really liked or found interesting here to further reinforce the learning process.

The "TIL" I wanted to note quickly today is how happy I was to see that Swift has something that's a reasonably recent addition to Emacs Lisp: conditional binding. Skipping off into Emacs Lisp for a moment, it would be very common for me to find myself writing something like this (this actually happens in all languages really):

(let ((foo (get-something-from-elsewhere)))
  (when foo
    (do-something-with foo)))

Quite simply: I'd get a value from elsewhere, that value could possibly be nil to mark that there was a failure to get a value, but that failure wasn't in any way fatal or even a problem worthy of note: I just needed to skip along. But that binding followed by the test was always a little jarring. And then, back in 25.1, if-let and when-let got added (of course, this being Lisp, it would have been very simple to add them anyway), and it was easier to write the code so it looked just a little nicer:

(when-let ((foo (get-something-from-elsewhere)))
  (do-something-with foo))

It's a small difference, but I find it a pleasing one.

So of course I was pleased to find that Swift has something similar with if and Optional values, where you can write something like:

if let foo = getSomethingFromElsewhere() {
    // Do something with foo but only if it's not nil
}

Which means you can do things like this (not that I'd really do things like this, but it was a handy test on the command line):

func oddRand() -> Int? {

    let n = Int.random( in: 0...99 )

    if n % 2 == 0 {
        return nil
    }

    return n
}

for _ in 0...10 {
    if let n = oddRand() {
        print( n )
    } else {
        print( "Nope" )
    }
}

As usual... of course that's horrible code, it was just thrown together to test/experience the language feature on the command line.

I like it though. I figure all the best languages have conditional binding. ;-)

Swift TIL 11

2 min read
TIL

This is one of those things, especially this little play to help appreciate the feature, that I'm filing under "kinda cool, but I am never doing this in production".

So Swift has operator overriding, and then some. Moreover, operators are, in effect, functions, just with some extra syntax support. None of this is new to me, I've worked in and with other languages that have this ability, expect... I don't ever recall working in a language that, at the time I did, supported creating brand new operators (okay, fine, Lisp is a bit of an outlier here and there's all sorts of fun conversations to be had there -- but still, let's stick with more "conventional" syntax here). Support always seemed to be about extending the ability of an existing operator.

Swift though... yeah, you get to pick from a huge character space when it comes to creating operators.

Which got me thinking... How cool would it be to have a prefix operator that turns a floating point number into a currency-friendly number (you know, the sort of number type that can be used for currency-friendly calculations).

Swift has the decimal type which, at first glance anyway, looks to be a sensible candidate; even if it isn't (and, really, how to actually sensibly handle currency is a whole series of blog posts in their own right, that I have no wish to write myself because such things are a previous working life for me, and other people have doubtless done a very comprehensive job elsewhere over the years) it will serve as a good stand-in for the little bit of horror I'm going to write next.

So... Let's say we want to use £ as a prefix operator to say "see this number? make it a decimal", we could do something as simple as this:

import Foundation

prefix operator £
prefix func £( n : Decimal ) -> Decimal { n }

print( £1.20 + £0.75 + £0.01 )

Horrifically and delightfully, it works:

$ swift run
[3/3] Linking opover
1.96

I know, I know, I feel bad for even trying. But it's also kinda cool that the language has this.

It gets better though...

While reading up on what characters can and can't be used as operators, one thing that stood out was the fact that, more or less, any character that isn't a valid identifier can be used as an operator. So... hang on, we can use "emoji" as identifiers?

Like this?

import Foundation

prefix operator £
prefix func £( n : Decimal ) -> Decimal { n }

let 💵 = £1.20 + £0.75 + £0.01

print( 💵 )

Why yes. Yes we can. 😈

When the man page fibs

3 min read

Earlier this week something in my development environment, relating to Homebrew, Python, pyenv and pipenv, got updated and broke a handful of repositories. Not in a way that I couldn't recover from, just in a way that was annoying, got in the way of my workflow, and needed attention. (note to self: how I set up for Python/Django development on a machine might be a good post in the future)

Once I was sure what the fix was (pretty much: nuke the virtual environment and recreate it with pipenv, being very explicit about the version of Python to use) the next step was to figure out how many repositories were affected; not all were and there wasn't an obvious pattern to it. What was obvious was that the problem came down to python in the .venv directory pointing to a binary that didn't exist any more.

Screenshot 2020-07-10 at 20.21.15.png

So... tracking down problematic repositories would be simple enough, just look for every instance of .venv/bin/python and be sure it points to something rather than nothing; if it points to nothing I need to remake the virtual environment.

I quickly knocked up a script that was based around looking over the results of a find, and initially decided to use file to perform the test on python. It seemed to make sense, as I wrote the script I checked the man page for file(1) on macOS and sure enough, this exists:

-E On filesystem errors (file not found etc), instead of handling the error as regular output as POSIX mandates and keep going, issue an error message and exit.

Given that file dereferences links by default, that should get me an error for a broken link, right? Bit hacky I guess, but it was the first thing that came to mind for a quick bit of scripting and would do the trick. Only...

$ file -E does-not-exist
file: invalid option -- E
Usage: file [bcCdEhikLlNnprsvzZ0] [-e test] [-f namefile] [-F separator] [-m magicfiles] [-M magicfiles] file...
       file -C -m magicfiles
Try `file --help' for more information.

Wat?!? But it's right there! It says so in the manual! -E is documented right in the manual page! And yet it's not in the valid switch list as put out by the command, and it's an invalid option. The hell?

So I go back and look at the man page again and then I notice it isn't in the list of switches in the synopsis.

SYNOPSIS
file [-bcdDhiIkLnNprsvz] [--extension] [--mime-encoding] [--mime-type] [-f namefile] [-m magicfiles] [-P name=value] [-M magicfiles] file
file -C [-m magicfiles]
file [--help]

I then did the obvious tests. Did I have file aliased in some way? No. Was some other thing that works and acts like file in my path? No. Was I absolutely 100% using /usr/bin/file? Yes.

Long story short: it seems the man page for file, on macOS, fibs about what switches it supports; it says that -E is a valid option, but it's not there.

What's even odder is the man page says it documents v5.04 of the command, but --version reports v5.37. Meanwhile, if I check on a GNU/Linux box I have access to, it does support -E, reports it in the switches, documents it in the man page (in both the synopsis and in the main body of the page) and it is v5.25 (and so is its man page).

So that was something like 20 minutes lost to a very small problem, for which there was no real solution, but was time that had to be spent to get to the bottom of it.

In the end I went with what I probably should have gone with in the first place: stat -L.

for venv in $(find . -name .venv)
do
    if ! stat -L "$venv/bin/python" > /dev/null 2>&1
    then
        echo "$(dirname $venv)"
    fi
done

And now I have that script in my ~/bin directory, ready for the next time Homebrew and friends conspire to throw my day off for a while.

Helping myself change the default git branch

2 min read

This is something I've being meaning to do for a couple or so years now, and unsurprisingly it's bubbled up again recently: the business of swapping the name of the master branch in git out for something better.

Because it's one of those jobs that's simultaneously simple and also laborious, I kept putting it off. Changing up the local configuration so that main (or whatever name you prefer) is used "out of the box" is simple enough; the laborious part is updating all of the repositories that live in the "forge of choice". In my case, over on GitHub, I have getting on for 200 repositories -- 142 of which are public (as of the time of writing). At work we use GitLab as our internal forge and I've got a non-trivial number of repositories on there too.

The obvious first step to tackling this is to knock up a little tool to help find the repos that still need swapping. That was simple enough:

#!/bin/bash

# Quick and dirty tool to find repositories that still make use of a
# "master" branch. Helps with tracking down the ones that need
# updating/improving.

for repo in $(find . -name .git)
do
    (
        cd "$(dirname $repo)"

        if git branch | grep master > /dev/null 2>&1
        then
            echo "$(dirname $repo)"
        fi
    )
done

### git-archaic ends here

It's not meant to be clever, just something I can run when I'm in a "default branch swapping" mood so find a repository or two to tackle. The idea being that it uses find to pull out any instance of .git in or below the current directory, changes to it (inside a sub-process to ensure the PWD gets put back after the cd that happens, before the next iteration of the loop), gets a list of the branches and, if master is one of them, prints the directory name.

Using this, I can now slowly work through my more active repositories and make the swap -- the idea being that if I currently have them cloned down to my machine, they're obviously some level of "active". At some point I imagine I could get more clever and use the APIs of the forges to look at all the repositories I own; that's another job for another day.

This gives me enough to be going on with. :-)

git

Swift TIL 10

2 min read
TIL

My leisurely journey into getting to know Swift by reading and then making notes to myself in my blog continues, and this weekend I encountered defer. As I was reading about Swift I did keep wondering when something like try (it has try), catch (it has catch) and finally (it doesn't have finally, but...) might crop up. This weekend I hit the part of the book that covered this sort of thing.

Given Swift's apparent general reliance on not throwing errors but instead using Optional and nil to signify issues, it sort of came as no surprise that its approach to implementing something like try...finally is actually divorced from try. I'm not sure how I feel about it yet, but defer makes sense.

Here's an utterly useless bit of code that demonstrates how it works:

func add( _ n1 : Int, _ n2 : Int ) -> Int {

    defer {
        print( "Huh! We did some adding!" )
    }

    print( "About to do the add and then return" )
    return n1 + n2
}

print( add( 2, 2 ) )

When run, the output is this:

$ swift run
[3/3] Linking try-defer
About to do the add and then return
Huh! We did some adding!
4

A defer (and there can be multiple) is tied to the block that it's declared in, and is executed when the block exits. This is, of course, going to be really handy for things like resource-management where you don't want to be leaking something, although I can imagine a few other uses too (none of which are really going to be novel for someone who's coded in other languages with similar constructs).

What I find interesting about this is that one or more defer blocks can be declared at various locations within a block of code; this obviously makes sense in that you might not want to be handling the tidy-up of something you've not got around to creating yet. But there's also part of me feels uneasy about the "exit" code being declared further up the body of code, rather than down towards the end. On the other hand I think I do appreciate the idea of, up front, writing "look, any time there's a GTFO in the code that follows, this will happen" -- you're made aware pretty quickly of what to expect.

Anyway, it's good to know Swift has something similar to a finally.