Complete and Await Reply Script, Version 1.2

January 22, 2017

Via email, Rachel Hopkin shared a cool feature request for my Complete and Await Reply script. Now the script can automatically set a defer date on the follow-up tasks it creates. If you run OmniFocus from the Forecast perspective, this is a great way to clear an item from today’s list after you’ve reminded someone about it.

This feature is off by default, but it’s easy to enable. See the instructions on the download page.

Share and enjoy!


Finder and Terminal are Friends

January 10, 2017

As developers, we tend to spend a lot of time typing in Terminal windows. Even so, I often find it more helpful to browse directories and files in Finder. I have three little hacks that simplify moving between the two modes.

pwdf

The first hack is a shell function that logs the path of the directory shown by the front-most Finder window.

# pwdf: echoes path of front-most window of Finder
pwdf ()
{ 
   currFolderPath=$( /usr/bin/osascript <<"   EOT"
       tell application "Finder"
           try
               set currFolder to (folder of the front window as alias)
           on error
               set currFolder to (path to desktop folder as alias)
           end try
           POSIX path of currFolder
       end tell
   EOT
   )
   echo "$currFolderPath"
}

I picked up the basic gist of this from a Mac OS X Hints post by Cameron Hayes in the long ago. A much belated thank you to Cameron.

I use zsh as my shell, so this function is declared in my ~/.zshrc. (I think the same function declaration would work in .bashrc, but it’s been six years since I used bash, so please correct me if I’m wrong.)

Add this function declaration and restart Terminal. Then open a Finder window, switch to Terminal, and type pwdf. The path to the Finder window’s folder will be logged:

curt@Fozzie-Bear% pwdf
/Users/curt/Documents/SourceDirs/curtclifton.net/

Where this gets really useful is wrapping the command in backticks to embed the result in other commands. For example, we can count the number of lines in all Swift files in the current Finder window:

curt@Fozzie-Bear% wc -l "`pwdf`"*.swift
      48 /Users/curt/Documents/SourceDirs/curtclifton.net/BlogGenApp/BlogGenApp/AppDelegate.swift
     170 /Users/curt/Documents/SourceDirs/curtclifton.net/BlogGenApp/BlogGenApp/BlogGenModel.swift
     488 /Users/curt/Documents/SourceDirs/curtclifton.net/BlogGenApp/BlogGenApp/SwiftyUserDefaults.swift
     177 /Users/curt/Documents/SourceDirs/curtclifton.net/BlogGenApp/BlogGenApp/ViewController.swift
      22 /Users/curt/Documents/SourceDirs/curtclifton.net/BlogGenApp/BlogGenApp/main.swift
     905 total

cdf

One use of pwdf comes up enough that I created an alias.

alias cdf='cd "`pwdf`"'

Add this to your .zshrc, being careful to get all the quotes and ticks leaning the right directions. Restart Terminal and then type cdf at a prompt to change your working directory to that of the front-most Finder window. I find this super handy when I’ve browsed through directories in a Finder window and want to work some Terminal magic on the files therein. Just ⌘-Tab to Terminal, type cdf, and go to town.

open .

Sometimes I want to move in the opposite direction, opening a Finder window on the directory that I’m working within in Terminal. The built-in Mac system command open solves this problem. If you pass a directory as the argument to open, it opens a Finder window. So, to start browsing your current working directory just:

curt@Fozzie-Bear% open .

Sometimes little hacks like these can make a big difference over time. And I get a real kick out of the fact that pwdf combines zsh and AppleScript.

What are your favorite little hacks? What do you like about them?


Variations on a Theme

January 4, 2017

In Weak References and Type Erasure I wrote about using closures, weak capture, and an isLivePredicate to combine type erasure and weak references.

The goal was to store a heterogeneous collection of generic Signal<Value> objects while letting them deallocate when client code was done with them. My final implementation used a SignalHolder struct with a couple of closures inside. One closure extracted a value from the current state of the data model and passed that value to a weakly captured generic signal. The other closure weakly captured the same signal and returned whether the signal was still non-nil. I used that isLivePredicate machinery to discard signal holders when their signals deallocated.

I had a nagging doubt that my solution was too clever. James Savage and Joe Groff replied on Twitter with a couple of other approaches that I thought were worth sharing.

Using NSHashTable

James suggested using NSHashTable to eliminate the need for my isLivePredicate machinery. We still need a signal holder for type erasure, but if we can tie the lifetime of the signal holder to that of the signal, we get removal of expired signal holders for free.

To tie the lifetimes together, we can make Signal hold a strong reference to its associated signal holder:

public class Signal<Value> {
    fileprivate var keepAliveReference: SignalHolderClass?

    // … continued as in previous post …
}

Because we’re going to put signal holders in an NSHashTable, we need to make them class instances instead of structs. They also lose the isLivePredicate machinery, but are otherwise unchanged:

class SignalHolderClass {
    private let caller: (DataStorage) -> Void

    init<Value>(
        signal: Signal<Value>,
        matching matcher: @escaping (DataStorage) -> Value?)
    {
        caller = { [weak signal] storage in
            guard let signal = signal else { return }
            if let value = matcher(storage) {
                signal.update(to: value)
            }
        }
    }

    func update(from storage: DataStorage) {
        caller(storage)
    }
}

Inside our data model, signalHolders becomes a hash table:

private let signalHolders = NSHashTable<SignalHolderClass>(options: [.objectPointerPersonality, .weakMemory])

NSHashTable has set semantics. The first option to NSHashTable’s initializer says that we want to compare objects by pointers. That’s the right semantics here, and thankfully so, because there’s no good way to do equality comparison on the contents of SignalHolderClass instances — closures aren’t Equatable.

The second option to the initializer says that the signal holders should be held weakly. The only strong reference to a signal holder is from the signal that it wraps.

The addSignal(_:, matching:) method is essentially unchanged in James’s approach, except that NSHashTable uses add() instead of append():

func addSignal<Value>(
    _ signal: Signal<Value>,
    matching matcher: @escaping (DataStorage) -> Value?)
{
    let holder = SignalHolderClass(signal: signal, matching: matcher)
    signalHolders.add(holder)
}

By eliminating the isLivePredicate machinery, the didSet handler for storage is straightforward:

var storage: DataStorage {
    didSet {
        for holder in signalHolders.allObjects {
            holder.update(from: storage)
        }
    }
}

The management of object lifetimes in this approach is more Cocoa-like. The pattern of cyclic references — a strong reference from Signal to SignalHolderClass and a weak reference back — is meat-and-potatoes stuff to any Cocoa developer familiar with the delegate pattern. One slight downside is that signalHolders now stores an object type instead of a value-typed Swift collection, but that’s really just an issue for purists. The collection is private to the data model instance and is never shared.

Unfortunately, there’s one deal breaker for my actual project. The Signal class is in a different framework than my data model and SignalHolderClass. So the keepAliveReference we added to Signal above would have to be implemented in an extension using a computed property and associated objects. Alternatively, we could jump through some hoops to move SignalHolderClass into the same framework as Signal without creating a mutual dependency via DataStorage, but I suspect that complexity is greater than the savings of eliminating the isLivePredicate machinery.

Using a Protocol

Joe approached the problem from the opposite direction. Instead of eliminating the isLivePredicate machinery, he suggested using a protocol to simplify the type erasure.

First we make SignalHolder a protocol:

private protocol SignalHolder {
    var isLive: Bool { get }
    func update(from storage: DataStorage)
}

This lets the signal holder storage in our data model just be an array of protocol witnesses:

private var signalHolders: [SignalHolder] = []

Next we make the old signal holder struct generic:

private struct SignalHolderImpl<Value>: SignalHolder {
    private let matcher: (DataStorage) -> Value?
    private weak var signal: Signal<Value>?

    init(
        signal: Signal<Value>,
        matching matcher: @escaping (DataStorage) -> Value?)
    {
        self.matcher = matcher
        self.signal = signal
    }

    var isLive: Bool { return signal != nil }

    func update(from storage: DataStorage) {
        guard let signal = signal else { return }
        if let value = matcher(storage) {
            signal.update(to: value)
        }
    }
}

Instead of storing type-erased closures, SignalHolderImpl directly stores the matching function and a weak reference to the signal. The bodies of the old caller and isLivePredicate closures move into the update(from:) method and isLive computed property respectively.

The addSignal(_:, matching:) method is unchanged from my implementation, as is the didSet handler for storage.

The downside of this approach is that we have to introduce an additional protocol and make the signal holder generic. That’s additional type system complexity. The huge win is that we stop using closures for type erasure. I’m certain that the protocol-oriented approach will be more familiar than nested closures to future developers coming to this code (no matter how much the old Schemer in me likes function composition).

It may also be the case that bouncing through the protocol witnesses to update all the signal holders is less performant than my closure-based approach. That’s not really a concern at all in this code. The number of active signals is limited in my project.

Epilogue

Practically speaking, I think all three approaches to this problem have about the same complexity. Maybe this is as simple as a solution can be here. Based on Swiftiness and the practical concerns regarding separate frameworks, I plan to convert my project to Joe’s SignalHolder protocol approach.

Thanks to James and Joe for their ideas and conversation. It’s good to be reminded to use both the tools that came before and the new hotness when approaching a problem.


Weak References and Type Erasure

January 2, 2017

In one of my side projects, two interesting Swift problems—heterogeneous arrays and weak references—collided in an interesting way.

I needed to store an array of signal objects.

var signals: [Signal] = []

Easy peasy? Not quite. Signals push updates to subscribers, so we’d really like them to be generic in the type of values that they push:

public class Signal<Value> {
    private var currentValue: Value? = nil {
        didSet {
            // … push new value to subscribers …
        }
    }

    func update(to value: Value) {
        currentValue = value
    }

    // … subscription machinery elided …
}

The Values here range over all the types stored in the model, plus arrays of those types. We could try something like:

var signals: [Signal<Any>] = []

but co- and contravariance raise their heads and we can’t actually put more specific signals in the array:

let intSignal = Signal<Int>()
signals.append(intSignal)

Playground execution failed: error: WeakReferencesAndTypeErasure.playground:27:16: error: cannot convert value of type 'Signal<Int>' to expected argument type 'Signal<Any>'

In situations like this, I often reach for type erasure.

For my project, it turns out that there is only one method on Signal that I need to call. For each signal, there’s an associated mapping function that takes the global data model and produces the new value for the signal: (DataStorage) -> Value? If that function returns a non-nil value, then the signal should be updated to the value.

So, instead of storing signals directly, let’s store an array of closures that do the right thing:

var signalCallers: [(DataStorage) -> Void] = []

func addSignal<Value>(
    _ signal: Signal<Value>,
    matching matcher: @escaping (DataStorage) -> Value?)
{
    let caller: (DataStorage) -> Void = { storage in
        if let value = matcher(storage) {
            signal.update(to: value)
        }
    }
    signalCallers.append(caller)
}

To store a signal, we now call the addSignal(_:, matching:) function. The function captures the signal inside the caller closure and saves the closure in the signalCallers array.

Now when my app mutates the global data storage, the model layer can iterate over the signalCallers and broadcast changes to the subscribers.

var storage: DataStorage {
    didSet {
        for caller in signalCallers {
            caller(storage)
        }
    }
}

Unfortunately we just created a signicant memory leak. The closures in signalCallers retain every signal. My data model retains signalCallers. Because the data model is a singleton, every signal stays in memory and is updated, even if the subscriber to the signal is long gone.

Subscribers should be responsible for retaining signals. The data model should just hold the signals weakly. We can change caller closure in `addSignal(_:, matching:) to use a capture list:

func addSignal<Value>(
    _ signal: Signal<Value>,
    matching matcher: @escaping (DataStorage) -> Value?)
{
    let caller: (DataStorage) -> Void = { [weak signal] storage in
        guard let signal = signal else { return }
        if let value = matcher(storage) {
            signal.update(to: value)
        }
    }
    signalCallers.append(caller)
}

This keeps us from leaking all the signals. Signals are only retained by their subscribers and, thanks to the guard, the caller closures become no-ops after the signals are deallocated. Unfortunately, we’re still leaking the closures themselves. We also have a performance problem. The signalCallers array is always growing. The longer the app runs, the more no-op callers we have to invoke on every model change.

How can we clean up the old callers? We need some logic that tells us when a signal has been deallocated. But we don’t have a direct reference to any signals at all from the data model. We gave that up when we used type erasure. The only references to the signals are inside the callers.

That realization leads to the insight that unlocks this problem. We can capture a weak reference to the signal inside another closure that tells us whether or not a signal has been deallocated:

let isLivePredicate: () -> Bool = { [weak signal] _ in
    return signal != nil
}

Let’s wrap that predicate and the caller closure up in a little struct:

private struct SignalHolder {
    private let isLivePredicate: () -> Bool
    private let caller: (DataStorage) -> Void

    init<Value>(
        signal: Signal<Value>,
        matching matcher: @escaping (DataStorage) -> Value?)
    {
        isLivePredicate = { [weak signal] _ in
            return signal != nil
        }

        caller = { [weak signal] storage in
            guard let signal = signal else { return }
            if let value = matcher(storage) {
                signal.update(to: value)
            }
        }
    }

    var isLive: Bool { return isLivePredicate() }

    func update(from storage: DataStorage) {
        caller(storage)
    }
}

We can replace our array of signal callers with signal holders and update addSignal(_:,matching:) to do the right thing:

private var signalHolders: [SignalHolder] = []

func addSignal<Value>(
    _ signal: Signal<Value>,
    matching matcher: @escaping (DataStorage) -> Value?)
{
    let holder = SignalHolder(signal: signal, matching: matcher)
    signalHolders.append(holder)
}

Finally, we can update the didSet handler for storage to filter out deallocated signals:

var storage: DataStorage {
    didSet {
        signalHolders = signalHolders.filter { $0.isLive }
        for holder in signalHolders {
            holder.update(from: storage)
        }
    }
}

Now every time there’s a model change, we dispose of the no-longer-useful signal holders, then update the remaining ones. Type erasure lets us store heterogeneous signals. Capture lists let us hold the signals weakly. The isLivePredicate lets us clean up after ourselves.

I’m mostly pleased with this solution, but I still have a nagging doubt that it’s too clever. Will future me be confused about what’s going on here? Or are type erasure and weak capture standard practice that Swift developers will just expect to understand? Is there a way to simplify this or make it clearer? I’d love to know.


The Carson Rationalization

November 14, 2016

Voting for Donald Trump was a racist act. Assuming the Trump voter was well-informed, it’s a simple case analysis:

  1. Either they directly supported his racist rhetoric. It follows that their support for him was a racist act.
  2. Or they decided that Trump’s racist rhetoric didn’t matter. That decision is also a racist act as it validates his rhetoric as acceptable.

John Scalzi lays out the argument in more detail in his excellent piece, “The Cinemax Theory of Racism”.

I’ve raised this point with several of my Trump-supporting family members. The response has been, “I supported Ben Carson in the caucuses1, so I can’t be racist.”

Without re-litigating Carson’s fitness for the Presidency, I want to point out three flaws in this rationalization.

First, racist is both a noun and an adjective. I have no qualms about applying the noun to the President-elect. He has a long history of acting on his racial prejudices. However, here I’m applying the adjective. Nearly all of us harbor prejudices against others. It’s a natural consequence of growing up in our society. Failing to recognize and counteract our own implicit biases leads us to engage in aversive racism. Pearson, et al write:

Aversive racists, in contrast, sympathize with victims of past injustice, support principles of racial equality, and genuinely regard themselves as non-prejudiced, but at the same time possess conflicting, often non-conscious, negative feelings and beliefs about Blacks that are rooted in basic psychological processes that promote racial bias … The negative feelings that aversive racists have toward Blacks typically do not reflect open antipathy, but rather consist of more avoidant reactions of discomfort, anxiety, or fear. That is, they find Blacks ‘aversive’, while at the same time find any suggestion that they might be prejudiced ‘aversive’ as well.

We need to recognize our capacity for aversive racism and actively counteract it.

Second, using support for Carson to argue that support for Trump wasn’t racist is a straightforward application of the one black friend argument.

The underlying fallacy is that one single point of data, this one “friend,” completely overrides any other bits of evidence we have to assess someone’s views. This is simply not valid reasoning.

Prejudiced views exhibit as a priori judgments against a group. Having come to support Carson after learning about his history and beliefs has nothing to say about whether or not ones initial perceptions reflected a racist bias. Having a racist bias may make it harder to come to support Carson, but having come to support him does not imply the absence of racist bias. (Neither does it imply presence of racist bias. Having a racist bias and supporting Carson are simply unrelated.)

Overcoming a racist bias to support Carson puts us at risk of the third flaw in the Carson rationalization, Carson as magical Negro. Jamil Smith, writing in the New Republic, calls this out far better than I could:

Carson fits the bill because his is a blackness of poor circumstances and personal responsibility, minus the racial grievances. He doesn’t complain about the structural inequality that his drive and skills enabled him to escape, at least financially. … Given his political performance and his career legacy, he’s an ideal conservative magical Negro for the Fox News era: A man … who performs the conservative ideal of racial progress, denigrating himself while remaining content to enable continued injustices.

Supporting Ben Carson does not relieve one of responsibility for supporting Trump. Support for Trump was a racist act. That does not make all of his supporters racist. It does mean that their behavior was racist. That’s a vital difference. We all make mistakes. The important part is to recognize our mistakes, own them, and do better.


Post-script: Enlightened readers will certainly find things I’ve gotten wrong here. Writing on race, especially from my position of privilege, is probably ill-advised, but I write to understand and sharpen my own thinking. If I’ve screwed up, I ask for your forgiveness, and hope you’ll help educate me.

1 All of my immediate family apart from my wife and me are still in Iowa.