Sublime Text Guide

I'm a big fan of the Sublime Text text editor, and I've become one of its biggest evangelists at Etsy. A few months ago I gave an internal presentation/tutorial session on it, and used those notes to create a guide on our internal wiki. I've ported that guide to my blog so I can spread the Good Word to others. Since the source code for my blog is available publicly, I'm also hoping that people will help make it even better.

So head over to my Sublime Text Guide and let me know what you think.

Speeding up Jekyll's LSI

Jekyll's Latent Semantic Indexing (LSI) indexes your posts to create the "Related Posts" section you see at the bottom of the individual post pages. However, if you have more than a handful of posts, it is very slow, and it seems to get exponentially slower as the number of posts increase. I ran it on this site, with ~140 posts, and I finally killed it after 3 hours.

There's a fix for this, though, buried in the README for classifier-reborn, the library used to do the post classification. Install GNU GSL and rb-gsl. On OS X with homebrew, it's as easy as:

brew install gsl
gem install rb-gsl

Indexing time for me went from, well, 3-∞ hours to less than 5 seconds.

Fun with Swift Extensions

As promised, a post on extensions in Swift.

This isn't going to be an in-depth tutorial. Extensions have been covered better elsewhere, most notably in Apple's excellent documentation. I just want to show a couple extensions I've written: Array#find, and comparison operators for NSDate.

Array#find

If you want to find a value in a Swift Array, you can use the global generic find function:

let arr = [4,8,15,16,23,42]
let idx = find(arr, 15)  // Some(2)

However, find only lets you search for a static value, and what I really wanted was the ability to find the first value that satisfied a test function. For example, say I want to find the first odd number in an array. Let's extend Array so we can do that.

extension Array {
    func find(test: (T) -> Bool) -> Int? {
        for i in 0..<self.count {
            if test(self[i]) {
                return i
            }
        }
        return nil
    }
}

let arr = [4,8,15,16,23,42]
let idx = arr.find { $0 % 2 == 1 }  // Some(2)

The extension defines a method, find, on Array, that takes as an argument a function mapping an element of type T to a Bool, and returns an optional Int. In other words, we pass in a test function, and it returns the index of the first value in the array for which that function is true. If no such values are found, it returns nil.

But wait! you say. Why write an extension when you can just add a new signature to the global find function so that it will take a test function rather than a value? FINE. Be that way.

You can, um, find the method signature of find in Apple's documentation:

func find<C: CollectionType where C.Generator.Element: Equatable>(domain: C, value: C.Generator.Element) -> C.Index?

That looks a bit complicated, and CollectionTypes and Generators are a topic for another day, but all you really need to know is that in order to conform to the CollectionType protocol, you need to define a startIndex and an endIndex, and be subscriptable. C.Generator.Element is the type of items in the collection. So instead of passing in a value of type C.Generator.Element to find, we want to pass in a test function that takes a C.Generator.Element and returns a Bool. The rest looks very much like our Array extension:

func find<C: CollectionType where C.Generator.Element: Equatable>(domain: C, test: (C.Generator.Element) -> Bool) -> C.Index? {
    for i in domain.startIndex..<domain.endIndex {
        if test(domain[i]) {
            return i
        }
    }
    return nil
}

let arr = [4,8,15,16,23,42]
let idx = find(arr, { $0 % 2 == 1 })  // Some(2)

Comparison Operators for NSDate

NSDate annoyingly does not implement the Comparable protocol, meaning that instead of:

if someDate <= anotherDate { ...

You have to do:

if someDate.compare(anotherDate) == NSComparisonResult.OrderedAscending || someDate.isEqualToDate(anotherDate) { ...

Let's fix that. According to Apple's docs, we only need to implement < and == to conform to the Comparable protocol. Pretty clever, since with just those two, you can derive <=, >, >=, and !=.

extension NSDate : Comparable {}

public func < (lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs.compare(rhs) == NSComparisonResult.OrderedAscending &&
        !lhs.isEqualToDate(rhs)
}

public func == (lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs.isEqualToDate(rhs)
}

The first line just says that we are extending NSDate to conform to the Comparable protocol. You can leave this out, but then you'll have to implement all the other comparison operators.

The rest is pretty straight-forward. The only curious bit is why the operators are implemented in the global scope, rather than within the extension. I.e. you'd think you could do this:

// This does not work!
extension NSDdate : Comparable {
  func < (other:NSDate) -> Bool {
    return self.compare(other) == ...

So why not? Operators aren't methods in Swift, but global functions. They have precedence over methods, and have varying rules around associativity. You can even make up your own operators if you want. Be sure to read NSHipster's great article on Swift operators.

Swift Compiler Bugs

Swift is a great language, and far more enjoyable to program in than Objective-C, but it is still in its infancy, and bugs abound. One of the more frustrating is compiler errors that are flat-out wrong. Take a look at this (in Xcode 6.1):

Could not find member

"Could not find member 'date'?" Trust me when I tell you that date is indeed a member of a Todo object. So what's going on? Let's tweak it a bit:

Cannot invoke

Ah, there we go. Turns out you can't actually compare dates like that (hmm...I smell a follow-up post on writing extensions). Here's how you do it:

Fixed

Here's another one. The following is essentially a no-op:

for i in 0..<0 {
    println(i)
}

That's just a for loop from zero up to, but not including, zero. And this code is fine, doing what it is supposed to do—nothing.

However, try making the ending value of the loop less than the starting value. This should be equivalent to the above:

for i in 0...(-1) {
    println(i)
}

The ... operator means "up to and including". So this should also be a no-op (and it is in most programming languages). But:

For Loop Boom

Even worse, it will actually crash on the first executable line of code:

For Loop Boom 2

None of this should scare you away from using Swift. It's a fun language to work with, and ends up being much more concise than Objective-C. Just keep in mind that there are still bugs to be worked out, and if you're getting errors that just make no sense, there's a fair chance you've hit one.

The Importance of Sucking

One of the defining charactaristics of a successful programmer is being OK with the feeling that you have no idea what you're doing. I've been programming for over 30 years and it still happens to me regularly. The key is pushing on through that dark period, trusting that eventually you'll catch a glimmer of light and it will slowly start to make sense.

There are a few tricks I've picked up to help me get over that initial hump. The first is to find a tutorial someone has written, and just follow along, just to get something working. Once I have something working, no matter how basic, not only do I start to get a better sense of how all the pieces fit together, but I can start tweaking it and learn from how it responds.

Can't find a good tutorial? Great! Here's your opportunity to give back, and write your own. There's really no better way to learn. Start with doing the most basic thing imaginable. Just get something working. Write down your steps as you go. Then add to it, write it down, repeat. You'll eventually have something coherent that you can post, and you'll earn a ton of internet karma points.

It also really helps if you have a specific project to work on, otherwise the tutorial can be a bit of a dead end, and your effort can feel purposeless. Don't have a project, and can't think of one? Rewrite something you've already written. Ask friends and family if there's an app they wish they had. Write a basic version of an app that you're too cheap to buy. Write a todo app.

Start small, though; the bigger the project, the less likely you are to finish it. Minimum viable product (MVP) isn't just Silicon Valley jargon; it applies equally well to personal projects. Do the simplest, dumbest thing possible. It's easy to iterate and improve once you have something working, and you can always throw it all away and rewrite it once you feel you know what you're doing.

The feeling of sucking at something and being completely lost is a part of learning. Everyone starts out that way. If you're actively avoiding sucking, you're not learning. Get lost. Seriously, find someplace to get yourself lost. You'll be better for it. Then tell us about it when you inevitably emerge from the darkness.