Swift: Occam’s Code Razor

pink-shaver-880219-m

I’m continually fascinated by how Swift compiler inferencing enables you to omit code details. When you assign a value to a variable, Swift infers the type on your behalf. The two following lines are therefore effectively identical.

var num : Double = 32.0
var num = 32.0

The floating point literal used in the assignment enables the compiler to infer the Double type in the second example.

Joe Groff explains that Swift infers prefixes when “your argument is unambiguously of a single type T or T? and there’s a static member that returns T.” Swift’s inference feature requires fewer type declarations and produces more succinct code than many other languages and inferencing doesn’t stop with numeric literals. A feature called implicit member expression tightens your enumeration and class method code.

I often use inferred prefixes with Auto Layout. The omitted attribute and relation enumeration prefixes lead to simple, readable code. Here’s a simple constraint that co-aligns two views’ leading edges.

let constraint = NSLayoutConstraint(item: view1, 
    attribute: .Leading, 
    relatedBy: .Equal, 
    toItem: view2, 
    attribute: .Leading, 
    multiplier: 1.0, 
    constant: 0.0)

Compare this to the fully qualified version. It is a lot wordier and, in my opinion, harder to follow. That’s because parameter names re-inforce each enumeration. This redundancy (such as attribute: NSLayoutAttribute, relatedBy: NSLayoutRelation)  creates visual code clutter.

let constraint = NSLayoutConstraint(item: view1, 
    attribute: NSLayoutAttribute.Leading, 
    relatedBy: NSLayoutRelation.Equal, 
    toItem: view2, 
    attribute: NSLayoutAttribute.Leading, 
    multiplier: 1.0, 
    constant: 0.0)

This parsimony-by-inference trick also applies to switch constructs. Case statements reduce to just the most meaningful enumeration elements.

switch attribute {
case .Left, .Leading, .Top:
    actualInset = -inset
default:
    actualInset = inset
}

You can apply this trick anywhere that the compiler can determine an implied enumeration type. You win by simplifying and clarifying your code.

Implicit member expressions also work with class methods. For example, you might set a view’s background color.

view0.backgroundColor = .redColor()

Swift knows that the assignment must use the UIColor class, so it infers that redColor() is a UIColor class method. This enables you to omit any explicit UIColor reference.

Here’s another example, one that creates and customizes a date formatter using both kinds of implicit member expressions.

var formatter = NSDateFormatter()
formatter.timeStyle = .ShortStyle
formatter.locale = .autoupdatingCurrentLocale()

In each case, the inferred type establishes enumeration or class that’s omitted from the code.

Type instances also use implicit inferences. As you define methods and properties,  Swift offers implicit access to instance methods and properties without a “self” prefix. Here’s a fairly simple example that extends a CGVector struct to return the vector magnitude and a unit vector.

public extension CGVector {
    public var magnitude : CGFloat {
        return sqrt((self.dx * self.dx) + 
            (self.dy * self.dy))
    }
    
    public var unit : CGVector {
        return CGVectorMake(self.dx / self.magnitude, 
            self.dy / self.magnitude)
    }
}

This example uses a very “Objective-C”-like style that explicitly refers to self to indicate property and method ownership. In Swift, you don’t need this. Simplify the implementation by killing all the “self.” references.

public extension CGVector {
    public var magnitude : CGFloat {
        return sqrt((dx * dx) + (dy * dy))
    }
    
    public var unit : CGVector {
        return CGVectorMake(dx / magnitude, dy / magnitude)
    }
}

The result is a simpler and cleaner but functionally equivalent implementation of these two properties. As a rule, you don’t need self except in initializers and closures. The compiler proactively reminds you when those explicit references are needed, so err on the side of simplicity.

Screen Shot 2015-04-21 at 8.46.37 AM

Thanks, Mike Ash, for the inspiration

6 Comments

  • I personally find Objective-C to be self documented when properly written, not wordy and easy to read and understand while also easy to debug is better than succinct to read or more poetic to write, see debugging Java 7 vs Java 8 code. Still, Swift can create some beautiful easy to read and maintain code too… FSM with Swift can be really nice and show the power of the new switch statements.

    • You’re totally right that readability and clarity of code is far more important than brevity. But expressing your thoughts succinctly (vs verbosely) also have a big effect on clarity. I wrote about this on my blog: http://radex.io/swift/methods/

      • One disagreement I would have with this is regarding the tip to omit the “self.” on property access. I myself much prefer using the “self.” prefix, as it makes it obvious to a reader of the code that this is accessing a property and not a local variable defined in the method somewhere. Particularly since variable declarations don’t have to be at the beginning of a code block with Swift and the modern C variants, this increases code clarity quite dramatically, in my view.

        • I think it’s a matter of method size. If you have a large method (30+ lines), then yeah, the extra bit of clarity from `self.` helps. But for the most part, you probably want to have ~10 line methods, and there I haven’t found any gain from typing extra “self”

  • “unit” is better described as “.normalized”. http://docs.unity3d.com/ScriptReference/Vector2-normalized.html

  • […] 的规范:“当编译器可以自动推断成员类型时,你就可以在使用隐式成员表达式时省略 […]

Leave a Reply