Properties vs. methods
· 2 minute read
Here’s a potentially controversial thought that keeps coming back to me as I spend more and more time developing in Swift: I don’t think that properties should be part of a type’s public interface.
The Swift Programming Language states that “Properties associate values with a particular class, structure, or enumeration.” This is all well and good, since associating types with values is not functionality that could otherwise be provided by methods alone. I certainly don’t have a problem with using properties to implement a type. I’m solely bothered by this implementation detail subsequently leaking out into a type’s API. Let’s take a look at an excerpt from String
’s public interface:
public struct String {
public var lowercaseString: String { get }
}
From this, a caller knows that stringInstance.lowercaseString
can be called without parentheses. On the other hand, the standard library authors could’ve just as easily implemented String
as follows:
public struct String {
public func lowercaseString() -> String
}
In which case, we’d call stringInstance.lowercaseString()
to achieve the same result. But why on Earth should a caller need to concern themselves with this distinction?
Over the years, I’ve heard many variations on the following: properties are for accessing instance variables, while methods are for performing actions. I have two major problems with this line of thinking:
- If properties are for accessing instance variables, having them be part of a class’s public API provides unnecessary transparency into how that class is implemented, violating encapsulation.
- We all know full well that properties aren’t just for accessing instance variables. Computed properties were and are commonly used in Objective-C. Swift even perpetuates this further by providing first-class language support for them1.
While Swift eschewed Objective-C’s brackets in favor of dot-syntax for everything, it didn’t actually shake having different invocation syntaxes for properties and methods. I wish it had.
Consistency when calling a property accessor or a method would be beneficial for other reasons as well. Every zero-argument method could be implemented as a property, but adding an argument would then change the call site from foo.bar
to foo.bar(argument: baz)
instead of from foo.bar()
to foo.bar(argument: baz)
. Isn’t that kind of weird?
Instance methods can also be curried, and in fact, are already curried class methods. You can get a handle to them as follows:
struct SomeType {
func doSomething() {
// ...
}
}
let somethingDoer = SomeType.doSomething
You can’t do this with properties though, meaning you can’t employ tricks like this with higher-order functions, the way that you could if they were argument-less methods.
Properties provide a hugely beneficial, expressive way to instruct the compiler how to generate boilerplate accessors and mutators for you. Swift expands on this by providing a whole slew of additional ways to not only define them, but also hook into changes being made2. I’m really glad I don’t need to implement all of this myself.
At the same time, I can’t help but feel like Swift is missing an opportunity to provide a conceptually simpler programming model by removing this distinction from its types’ public interfaces.
@jansichermann @mb Believe me, we got there
— Bryan Irace (@irace) March 4, 2014
-
Calling Objective-C methods like
-[NSArray count]
using dot-notation was a surefire way to be chided, due tocount
not having been a property. Then Apple changedcount
to be a property instead of a method, ostensibly becausearray.count
is nicer than[array count]
. In the process, I’m assuming they didn’t stop computing the count in favor of storing it in an instance variable. ↩ -
As an aside, I do worry that properties are overused as a result of how nice it is to define them, syntactically. It’s incredibly tempting to have a
user
property whosedidSet
block updates a persistent store and notifies a delegate, when a method named something likepersistUserAndUpdateDelegate:
is far more informative to someone reading the call site. I like these two posts on the subject matter. ↩