A definition of functional programming is very hard to pin down.
Many introductions to functional programming start with map
and other higher-order functions, and never really draw the rest of the owl. Since these tools are available even in the most staunchly object-oriented languages, like Ruby, they don’t make for a particularly good definition of functional programming.
The defining component is sometimes simple: parameter order. While I’ve argued before that we should get rid of positional parameters entirely, they’re still an important part of programming today. What’s the difference between f a b
and a.f(b)
? It’s really just the order of the parameters. (I would also like to submit [a f:b]
for examination 😈)
Some definitions of functional programming are more encompassing. The opening paragraph of this Wikipedia article, for examples, suggests that functional programming primarily has to do with math. Languages that are particularly type-heavy, like ML and Haskell, with their monoids and monads, bear this out.
Others define functional programming primarily in terms of immutability, viewing functions as a means to that end. There are posts describing functional programming that talk about immutability before any other feature of the paradigm, including actually using functions.
Some take the immutability claim further. For them, functional programming requires purity of functions, also known as referential transparency — a lack of globals and a lack of side effects. While this purity forms the central tenet of their functional programming, a language like Lisp, the prototypical functional language, has immutability but doesn’t prevent side effects.
Finally, some people think it means using a particular style of programming called Reactive, a set of monads from the functional programming world that are grafted object-oriented languages with libraries such as RxJava and ReactiveCocoa.
This list of defintions itself isn’t complete, but provides a rough outline of the surface of functional programming. Certainly, the variety of styles available even within functional programming belies the blurriness and complexity of the field.
There’s a few of lessons to learn from a list like this. First, people define functional programming in terms of the elements of their favorite functional language or, in some cases, style. If they’re Haskell writers, referential transparency might be the component they focus on. If they’re writing Reactive Cocoa in Swift, that particular mode of programming defines what functional programming is for them.
Second, each of these definitions implies a different prescription for what makes good code. In each case, the practitioners make an argument for why their particular style of functional programming will solve your problems. It’s clear, however, from the breadth of different functional styles and values that there’s no one “real” functional programming. Anyone claiming to sell you the One True Solution to your programming woes should be scrutinized.
Lastly, and there seems to be no way around this: programming is complex. Whatever style of programming you like to use, you’re going to run into complexity. Whether your language lays that complexity bare (like FP), or gives you the tools to abstract it away (like OOP), you’re going to have to roll up your sleeves and apply elbow grease. No programming style will make that go away.
A core feature of Cocoa is class clusters. Class clusters are a way of letting a developer mimic a built-in core type by subclassing it and overriding its fundamental methods, called “primitive methods”.
Primitive methods are the key to how class clusters work. The simplest example of this is NSArray
.
On NSArray
all methods are guaranteed to be written in terms of the two primitive methods, -objectAtIndex:
and -count
. That means that if we can subclass NSArray
and override those two methods, and get all of the other NSArray
methods for free.
To illustrate, let’s use array reversal. The typical way in Cocoa of reversing an array is via the array’s reverseObjectEnumerator
. It could go in a category:
@implementation NSArray (Reversing)
- (NSArray *)reversedArray {
return self.reverseObjectEnumerator.allObjects;
}
@end
While this is a fine solution, if you examine the result of this, perhaps by checking its class in the debugger:
(lldb) po [self.reversedArray class]
__NSArrayM
You can see that it returns an __NSArrayM
, which is shorthand for NSMutableArray
. This means that the reversed array is not only mutable, but also, it represents a full copy of the old array. For long arrays, this can be expensive!
With class clusters, we can do something a little different. It’s a little bit more code, but it will reveal additional efficiency. Let’s reverse the array using a new class.
@interface ReversedArray : NSArray
@property (readonly) NSArray *arrayToReverse;
@end
@implementation ReversedArray
- (instancetype)initWithArray:(NSArray *)array {
self = [super init];
if (!self) return nil;
_arrayToReverse = [array copy];
return self;
}
- (NSUInteger)count {
return self.arrayToReverse.count;
}
- (id)objectAtIndex:(NSInteger)index {
return [self.arrayToReverse objectAtIndex:self.count - index - 1];
}
@end
That’s it. This is a fully-functioning NSArray
. You can use array subscripting, -indexOfObject:
, enumerators, sorting, all of it.
Add an extension, and you can use it exactly how you’d want:
@implementation NSArray (Reversing2)
- (NSArray *)reversedArray {
return [[ReversedArray alloc] initWithArray:self];
}
@end
At first blush, it’s an extreme solution to a simple problem. But there are certain advantages conferred here that I love.
First, this is a fully encapsulated implementation. The implementation can generate its backing array eagerly or lazily, depending on constraints of the device or the whims of the developer. The implementation details are hidden away, as it should be.
Second, we gain an edge with performance. The reverseObjectEnumerator
makes a full copy of the array. For large arrays, the class clustered version will use less memory and take less execution time. While we do call -copy
in our class cluster, assuming the array is already immutable that method will just return self
. (If it’s not immutable, -copy
will create a full copy of the array, and we’ve lost nothing with the new solution.)
If you’re still not convinced, you might be interested to know that Apple does it this way themselves. Try it out in your debugger. First, remove any categories that add -reversedArray
. Then, attempt to reverse an array in the debugger.
(lldb) po [@[@1, @2, @3] reversedArray]
(3, 2, 1)
You have to do this in the debugger, because the implementation of -reversedArray
isn’t exposed in the NSArray
headers, even thoughtit does exist (as of the iOS 9.0 SDK, at least). And calling it returns a specialized class, __NSArrayReversed
:
(lldb) po [[array reversedArray] class]
__NSArrayReversed
This specialized version behaves just like the version we implemented above.
One other thing that specialized subclasses allow you do is implement requirements more efficiently. With class clusters, any methods that you want to further optimize you can just re-implement on the subclass. For example, reversing a reversed array is just the original array.
@implementation ReversedArray (Specializations)
- (NSArray *)reversedArray {
return self.arrayToReverse;
}
@end
Class clusters and primitive methods exist for many core types, including NSArray
, NSString
, NSDictionary
, and their mutable counterparts.
On NSString
, methods like -stringByAppendingPathComponent:
can return a specialized version of NSString
that is backed by an array of path components, so that adding and removing path components is as easy as modifying that internal array. Let’s check if this is how it’s implemented:
(lldb) po [[@"a" stringByAppendingPathComponent:@"b"] class]
NSPathStore2
NSPathStore2
is a class-clustered NSString
exhibits these propreties. The implemenation can be optimized so that this class doesn’t have to search for path separators (like “/” or “:”), and it can manipulate paths more easily. (I wonder what happened to NSPathStore1
?) You can see all of the class clusters that Apple uses in this gist.
This is a cool tool for your toolbelt. What else can we use this technique for?
The project I’m currently working on has an SDK. I’ve used bad third-party SDKs before, and I wanted the standards for this project to be higher. I want the developers who are integrating this SDK into their project to rest easy, so the SDK has no categories at all. This was a problem for me, since I wanted to use -map:
on my arrays.
This technique is how I got out of that pickle. A class-clustered array that is initialized with a “before” array and a transformation block is all this class needs.
@interface MappedArray ()
@property (nonatomic) NSArray *backingArray;
@end
@implementation MappedArray
- (instancetype)initWithArray:(NSArray *)array transformationBlock:(id (^)(id object))block {
self = [super init];
if (!self) return nil;
NSMutableArray *mappedArray = [NSMutableArray arrayWithCapacity:array.count];
for (NSInteger i = 0; i < array.count; i++) {
[mappedArray addObject:block(array[i]) ?: [NSNull null]];
}
_backingArray = [mappedArray copy];
return self;
}
- (NSUInteger)count {
return self.backingArray.count;
}
- (id)objectAtIndex:(NSUInteger)index {
return [self.backingArray objectAtIndex:index];
}
@end
Notice here that while the array is backed by a mutable array, it doesn’t expose that in any meaningful way. A default implementation of -map:
in a category looks like:
- (NSArray *)map:(id (^)(id object))block {
NSMutableArray *mappedArray = [NSMutableArray arrayWithCapacity:self.count];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id value = block(obj) ?: [NSNull null];
[mappedArray addObject:value];
}];
return mappedArray;
}
With an implementation like this, you can forget to call -copy
on mappedArray
before returning it. It’s easy to forget that -copy
, which I know because I’ve done it. If you don’t copy
, you’re actually returning an array that can be accidentally cast to a mutable array. With MappedArray
that can never happen, because the mutation methods aren’t even implemented.
The class cluster way is definitely more code, but it’s more manipulable than the simple category. It can be lazily-loaded (Gist with sample implementation) if you need those performance characteristics. Lazy loading can never be done with the category way. And if you prefer your interface to be a method on NSArray
, you can still use the class cluster method from within a category:
- (NSArray *)map:(id (^)(id object))block {
return [MappedArray alloc] initWithArray:self transformationBlock:block];
}
The last use for this technique that I want to cover is domain-aware arrays and collections. Because each class cluster is simultaneously an NSArray
and whatever its subclassed type is, these arrays can be used in both contexts.
If you had a payroll application, you could create an EmployeesArray
class that acts as NSArray
. In the same way that -count
is a useful method for all arrays, your EmployeesArray
can have methods named -totalSalary
or -highestPaidEmployee
. While you could use KVC collection operators or -reduce
to the same end (and perhaps it would be implemented with one of those under the hood), this technique provides you with well-named, well-typed messages. It allows you to be more expressive and clear in your code, and that’s always awesome.
Class clusters are an awesome and underused Cocoa feature. These few techniques can help you realize performance and expressiveness gains at the same time.
In preparation for the monad post (and also for some life goals), I’ve been working on learning Haskell. What a weird language.
First thing’s first. I learned using Haskell for Mac and two books. First, I tried using Real World Haskell, but didn’t find it terribly effective. Learn You a Haskell, on the other hand, I thought was extremely well-written and effective at teaching the priniciples. However, I’ve been trying to read Haskell tutorials (and monad explanations) for a few years, and it’s hard to say how much that stuff primed me for being able to understand the language.
I finally feel like I can read Haskell (even if it’s with a little trouble), but I’m not writing it super well yet. Nevertheless, this feels like a victory.
I want to skip all the stuff about functional purity, static compilation, strong types, currying, and lazy evaulation. I’ve read a lot of blog posts about that kind of thing, and I don’t think there’s anything to be gained from repeating the work of others.
I’m going to talk about some stuff which struck me while learning Haskell. This post is meant for people who write code in languages that are not Haskell, but would like to learn more about the experience of learning it. Like many of my posts, it’s for past versions of myself.
Whitespace
Haskell is whitespace significant. This was the most unexpected thing I came across while learning Haskell. I thought we were all in agreement that Python would be the only programming language that maintains structure through whitespace so that we could all ceaselessly make fun of it. Apparently Haskell is also in this hilarious crowd of languages.
It seems like a goal of Haskell is to minimize the amount of punctuation that’s used to write code, and being whitespace significant helps with that.
Elegance
A lot of elegance is borne out of the way that functions are defined in Haskell (and in part I think to its aversion to punctuation). For example, if you wanted a function that filtered out all of the odd numbers in a list and left only the even ones, you could define it “strictly”, with xs
as an explicit parameter:
filterEvens xs = filter even xs
or more “loosely”:
filterEvens = filter even
Because of partial application, we can define filterEvens
as a partially-applied version of filter
. Since that just returns another function, we can just assign it to the name filterEven
and be done.
Because braces or other separating punctuation are optional, creating a function feels a lot more like definition, rather than implementation. These types of functions really lends themselves to succinct, if not one-line, definitions. I think creating functions in this style is a big part of the reason why Haskell’s champions describe it as declarative rather than imperative.
Haskell seems to “fit together” really nicely in a lot of ways. Instead of separating the idea of “blocks” from the idea of “functions”, they’re all the same thing. Anywhere you need a “block” (like the first parameter of filter
), you can just pass a function that has the correct type signature.
Another awesome example of this elegance is the quicksort implementation:
quicksort [] = []
quicksort (head:tail) =
let
smallerNumbers = quicksort (filter (<=head) tail)
biggerNumbers = quicksort (filter (>head) tail)
in
smallerNumbers ++ [head] ++ biggerNumbers
It’s embarrassing to admit, but I’ve never understood how quicksort works. Looking at the Haskell implementation, however, makes it painfully obvious.
Quicksort for an empty array is an empty array.
Quicksort for an array with a head and a tail is all the numbers that are smaller than the head, then the head, and then all the numbers that are larger than the head.
(Strictly speaking, this implementation isn’t quicksort exactly. A true quicksort implementation requires mutation, something that Haskell steers you away from. Much like the Sieve of Eratosthenes, a real implementation of quicksort in Haskell is much messier. This is a pedantic point.)
Another great example of elegance in Haskell is foldl
and foldr
. These are the Haskell equivalents of reduce
, which start from either the left of a list or the right.
foldl f initial [] = initial
foldl f initial (head:tail) = foldl f (f initial head) tail
foldr f initial [] = initial
foldr f initial (head:tail) = f head (foldr f initial tail)
They’re defined recursively, and that lends itself to great simplicity. They’re also both defined using the same components, just in a different order. I find myself having to stare at them for a while trying to parse exactly how they work, but they do work. I like the way they parallel each other.
Inelegance
There are also a few things about Haskell that strike me as extremely inelegant. I’ve gotten past the weird operators and the dense mathematical jargon. Again, those have been covered endlessly elsewhere so I’d rather not repeat other people’s work.
I think my expectation was to find no flaws in Haskell. That was obviously a doomed expectation, but I had hoped that since it was designed in the academy to be as weird and experimental as possible, that the creators wouldn’t make any of the terrible compromises that are so apparent in the rest of our programming languages. Haskell is an old language (about as old as Objective-C), and so it will have warts and cruft in it, like any older language.
On of the big things I found that seemed unfinished was the distinction between the identical map
and fmap
. map
is used exclusively for arrays, and fmap
is used for the more general Functor
types. (There’s also an identical function called liftM
for monads, lending almost PHP-esque levels of inconsistency.) These functions all do the exact same thing, but they have different names.
There are a lot of excuses given to explain this insconsistency, including path-dependence and better error messages, but it’s still the class of thing I would expect a language like Haskell to get right.
Another example of inelegance in Haskell is infix notation. The lanugage primarily operates with prefix functions. That means that, for example, the function div
, which divides two integers, is called like so:
div 92 10
For a lot of functions, this is hard to read. English is primarily a subject-verb-object language, so English speakers find “infix notation” easier to read in some cases. If you want to convert the div
function to infix style, you can surround it with backticks.
92 `div` 10
I think this is pretty ugly, and it feels like it was glommed on to the language in some kind of compromise. For div
specifically, you can use the operator /
to achieve the same thing, but this won’t work for all other functions.
There are also other operators like .
(compose), <$>
(apply), and $
(low-precedence apply), which allow you to change the order of functions and parenthesis usage. I’m not sure if it becomes easier to read this kind of Haskell over time, but for now, it’s very confusing to figure out how all the functions compose and apply to each other to build bigger functions.
The final thing that stands out about Haskell was the positional arguments. I’m more convinced now than ever that positional (as opposed to named) arguments are a huge flaw in the way we program. For example, the Haskell function elem
returns whether an element exists in a list. Do you call it with the list first or thing to search? There’s no way to know. (The thing to search comes first.)
This is even more important for partial application (or currying). If I want to make a function that searches many lists for the existence of the element 3
, I can curry the elem
function, like so:
existenceOfThree = elem 3
existenceOfThree [1,2,3] // => True
But if I want a function that searches the same list for many things, I have to either define my own function, or I have to use the function flip
, which flips the order of the inputs:
existsInMyList = flip elem [1,2,3]
And if there’s more than two arguments, it gets even worse.
Writing code like this is when I feel like I’m implementing rather than defining. This code is heading in the direction of imperative, where I have to worry about implementation details.
While I was researching this example, I found the source for both elem
and flip
:
elem = any . (==)
flip f x y = f y x
Look how nice those are! I want all the code I write to be that nice.
Positional arguments are part of how Haskell maintains its terseness, but they’re hard to read, impenetrable for beginners, and cause messiness when we want to apply parameters in a different order.
Haskell and You
The creators of Swift say they picked the best features from all the languages they could find, and a lot those features and functionalism come from Haskell. It’s definitely worth a weekend to take a stab at Learn You A Haskell and see where those ideas come from.
I wrote about coordinators at the beginning of the year, but the idea has matured a lot since then, and I’d like to reintroduce the topic with all of my learnings from the last few months.
This is adapted from a talk I gave at NSSpain this year. You can find the slides here. You can find the video here.
Three Problems
Overstuffed App Delegates
Apple does a really poor job of guiding us into putting code in good places. It’s really up to us to figure out how to structure our apps. The first place this is apparent is the app’s delegate.
The app delegate is the entry point into any app. Its primary responsibility is shuttling messages back and forth from the operating system to the app’s subsystems. Unfortunately, because of its position at the center of everything, it’s extremely easy to just plop stuff in here. One casualty of this strategy is the root view controller’s configuration. If you have a tab bar controller as the root of your app, you have to set up all the tab bar controller’s children somewhere, and the app delegate is as good a place as any.
In the first app that I made (and I suspect this is true for many of my readers as well), I put all the set up for my root view controller right in my app delegate. That code doesn’t really belong here, and it’s only there out of convenience.
I realized that after I made my first app, and I grew wiser. I did this trick:
@interface SKTabBarController : UITabBarController
I would create a subclass of the root view controller I wanted to use, and I would tuck my code away in there. It was a temporary patch over the problem, but ultimately, it’s not the right place for this code either. I would propose that we examine this object, the root view controller, from a perspective of responsiblities. Managing the child view controllers is within those duties, but allocating and configuration them not as much. We’re subclassing a thing that was never intended to subclassed, just so we can hide away some code that doesn’t have a home.
We need a better home for this app configuration logic.
Too Many Responsibilities
Here’s another confounding problem. In the same way that its easy to dump tons of responsibilities into the app delegate, each individual view controller also suffers.
A small selection of the stuff we have view controllers do:
Model-View Binding
Subview Allocation
Data Fetching
Layout
Data Transformation
Navigation Flow
User Input
Model Mutation
and many more besides
I came up with ways to tuck away these responsiblities in the children of each view controller in 8 Patterns to Help You Destroy Massive View Controller. All of these responsiblities can’t be in one place. It’s how we end up with 3000 line view controllers.
Which of this stuff should actually be in this class? Which should be elsewhere? What is the view controller’s job? It’s not clear.
There’s a great quote by Graham Lee that I love.
When you get overly attached to MVC, then you look at every class you create and ask the question “is this a model, a view, or a controller?”. Because this question makes no sense, the answer doesn’t either: anything that isn’t evidently data or evidently graphics gets put into the amorphous “controller” collection, which eventually sucks your entire codebase into its innards like a black hole collapsing under its own weight.
What the hell is a view controller? Controllers in the Smalltalkian sense were originally intended strictly for user input. And even the word “control” screws us. As I’ve written before:
When you call something a Controller, it absolves you of the need to separate your concerns. Nothing is out of scope, since its purpose is to control things. Your code quickly devolves into a procedure, reaching deep into other objects to query their state and manipulate them from afar. Boundless, it begins absorbing responsibilities.
Which of those responsibilities should be at the “controller level”? Graham’s right, the question doesn’t even make sense. Because we’re stuck with this awful word, we have to be really careful with what we allow our view controllers to do. If we don’t, it’s black hole central.
We also need help with this.
Smooth Flow
The last problem I want to discuss is navigation flow.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
id object = [self.dataSource objectAtIndexPath:indexPath];
SKDetailViewController *detailViewController = [[SKDetailViewController alloc] initWithDetailObject:object];
[self.navigationController pushViewController:detailViewController animated:YES];
}
This is a pretty common snippet of code, and I believe it’s in the Apple templates as well. Unfortunately, it’s garbage. Let’s go through it line-by-line:
id object = [self.dataSource objectAtIndexPath:indexPath];
This first line is fine. The dataSource
is a logical child of the view controller, and we’re asking it for the object we need to refer to.
SKDetailViewController *detailViewController = [[SKDetailViewController alloc] initWithDetailObject:object];
This is where things start getting a little hairy. The view controller is instantiating a new view controller, the next one in the chain, and configuring it. The view controller “knows” what’s coming up next in the flow. It knows how that thing is to be configured. The view controller that’s doing the presenting knows a ton of detail about where it exists in the world of your app.
[self.navigationController pushViewController:detailViewController animated:YES];
The third line is where it totally goes off the rails. The view controller is now grabbing its parent, because remember, these view controllers exist in a hierarchy, and then it’s sending a precise message to its parent about what to do. It’s bossing its parent around. In real life, children should never boss their parents around. In programming, I would argue children shouldn’t even know who their parents are!
In 8 Patterns to Help You Destroy Massive View Controller, I suggested a Navigator
type that can be injected into view controllers that contains the logic for moving through your app. Navigators are a fine solution if you need it in one place, but we quickly run into an issue that navigators can’t help us with.
Those three lines have a lot of responsibilities in it, but that one view controller isn’t the only place that this is happening. Imagine you have a photo editing app.
Your PhotoSelectionViewController
presents your StraighteningViewController
which presents your FilteringViewController
which presents your CaptioningViewController
. Your navigation flow is now spread among three different objects. Further, something is presenting your PhotoSelectionViewController
, but the dismissal has to be handled in CaptioningViewController
.
Passing a Navigator
around keeps these view controllers all coupled together in a chain, and doesn’t really solve the problem of each view controller knowing about the next one in the chain.
We also need help solving this problem.
Libraries vs Frameworks
I think Apple expects us to write code in all these ways. They want us to make the view controller the center of the world, because apps written all in the same style let them make the most impact with their SDK changes. Unfortunately, for developers, that’s not always the best move for us. We’re the ones who are responsible for maintaining our apps into the future, and dependable design and malleability of code are much higher priorities for us.
They say the distinction between libraries and frameworks is that you call libraries, and frameworks call you. I want to treat 3rd-party dependencies as much like libraries as possible.
When using UIKit
, you’re not in charge. You call -pushViewController:animated:
and it does a bunch of work and at some indeterminate time in the future, it calls -viewDidLoad:
on the next view controller, where you can do some more stuff. Rather than let UIKit
decide when your code runs, you should get out of UIKit
-land as soon as possible, so you can have full control over how your code flows.
I used to think of view controllers as the highest level thing in app, the things that know how to run the whole show. But I started wondering what it might look like to flip that around. A view is transparent to its view controller. It’s bossed around by its view controller. What if we made the view controller just another thing that’s transparent in the same way?
Coordinators
What is a coordinator?
So what is a coordinator? A coordinator is an object that bosses one or more view controllers around. Taking all of the driving logic out of your view controllers, and moving that stuff one layer up is gonna make your life a lot more awesome.
It all starts from the app coordinator. The app coordinator solves the problem of the overstuffed app delegate. The app delegate can hold on to the app coordinator and start it up. The app coordinator will set up the primary view controller for the app. You can find this pattern in the literature, in books like Patterns of Enterprise Application Architecture. They call it the Application Controller. The app coordinator is a special version of an application controller, specifically made for iOS. The app coordinator can create and configure view controllers, or it can spawn new child coordinators to perform subtasks.
Which responsiblities do coordinators take over from the view controller? Primarily, navigation and model mutation. (By model mutation I mean saving the user’s changes to the database, or making a PUT
or POST
request to an API, anything that can destructively modify the user’s data.)
When we take those tasks out of a view controller, we end up with a view controller that’s inert. It can be presented, it can fetch data, transform it for presentation, display it, but it crucially can’t alter it. We know now that any time we present a view controller, it won’t take things into its own hands. Whenever it needs to let us know about an event or user input, it uses a delegate method. Let’s take a look at a code example.
Code Example
Let’s start from the app delegate.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.rootViewController = [[UINavigationController alloc] init];
self.appCoordinator = [[SKAppCoordinator alloc] initWithNavigationController:self.rootViewController];
[self.appCoordinator start];
[self.window makeKeyAndVisible];
}
The app delegate sets up the apps window and root view controller, and then fires up the app coordinator. The coordinator’s initialization is separated from starting its work. This lets us create it however we want (lazily, greedily, etc), and only start it when we’re ready.
The coordinator is just a simple NSObject
:
@interface SKAppCoordinator : NSObject
This is great. There’s no secrets going on here. The UIViewController
class is thousands of lines, and we don’t know what exactly will happen when we call any of its methods, because it’s closed-source. Making the objects that run our app simple NSObject
types makes everything simpler.
The app coordinator initialized with the data it needs, which includes the root view controller.
- (instancetype)initWithNavigationController:(UINavigationController *)navigationController {
self = [super init];
if (!self) return nil;
_navigationController = navigationController;
return self;
}
Once we hit the -start
method, the coordinator sets off to work.
- (void)start {
if ([self isLoggedIn]) {
[self showContent];
} else {
[self showAuthentication];
}
}
Right from the start, coordinator is making decisions. Previously, logic like this didn’t have a home. You could maybe stick it in a view controller or in the app delegate, but those both have their flaws. In a view controller, you have one view controller which is making some decisions well-beyond its purpose, or you’re polluting the app delegate with stuff it doesn’t care about.
Let’s examine the -showAuthentication
method. Here, our base coordinator spawns off child coordinators to do work and subtasks.
- (void)showAuthentication {
SKAuthenticationCoordinator *authCoordinator = [[SKKAuthenticationCoordinator alloc] initWithNavigationViewController:self.navigationController];
authCoordinator.delegate = self;
[authCoordinator start];
[self.childCoordinators addObject:authCoordinator];
}
We use an array of childCoordinators
to prevent the child coordinators from getting deallocated.
View controllers exist in a tree, and each view controller has a view. Views exist in a tree of subviews, and each subview has a layer. Layers exist in a tree. Because of this childCoordinators
array, you get a tree of coordinators.
This child coordinator will create some view controllers, wait on them to do work, and signal us when it’s done. When a coordinator signals that its finished, it cleans itself up, popping off whatever view controllers it has added, and then uses delegates to get messages back up to its parent.
Once we’ve authenticated, we’ll get a delegate message, and, we can allow the child coordinator to be deallocated, and then we can get back to our regularly-scheduled programming.
- (void)coordinatorDidAuthenticate:(SKAuthenticationCoordinator *)coordinator {
[self.childCoordinators removeObject:coordinator];
[self showContent];
}
Inside the authentication coordinator, it creates whatever view controllers it needs, pushes them onto the navigation controller. Let’s take a look at what that looks like.
@implementation AuthCoordinator
- (instancetype)initWithNavigationController:(UINavigationController *)navigationController {
self = [super init];
if (!self) return nil;
_navigationController = navigationController;
return self;
}
Initialization is similar to the app coordinator.
- (void)start {
SKFirstRunViewController *firstRunViewcontroller = [SKFirstRunViewController new];
firstRunViewcontroller.delegate = self;
[self.navigationController pushViewController:firstRunViewcontroller animated:NO];
}
Authentication needs to start with a “first run view controller”. This view controller has buttons for sign up and log in and maybe a little slideshow explaining the app. Let’s push that view controller on and become its delegate.
This view controller has a delegate so we can be informed when the user taps the “sign up” button. Instead of the view controller needing to know which signup view controller to create and present, the coordinator will handle that.
- (void)firstRunViewControllerDidTapSignup:(SKFirstRunViewController *)firstRunViewController {
SKSignUpViewController *signUpViewController = [[SKSignUpViewController alloc] init];
signupViewController.delegate = self;
[self.navigationController pushViewController:signupViewController animated:YES];
}
We become the sign up view controller’s delegate so that it can inform us when its buttons are pushed.
- (void)signUpViewController:(SKSignUpViewController *)signupViewController didTapSignupWithEmail:(NSString *)email password:(NSString *)password {
//...
}
And so on. Here we actually perform the work of the signup API request and saving the authentication token, and then we inform our parent coordinator.
Whenever anything happens with a view controller (like user input) the view controller will tell its delegate (in this case the coordinator) and the coordinator will execute the actual task that the user intended. It’s important to have the coordinator do the work, so that the view controller remains inert.
Why are coordinators great?
Each view controller is now isolated.
View controllers don’t know anything beyond how to present their data. Whenever anything happens, it tells its delegate, but of course it doesn’t know who its delegate is.
Before, when there was a fork in the road, the view controller needs to ask “Okay, well, am I on the iPad or the iPhone?”. “Is the user being A/B tested?” They no longer have to ask any questions like that. Have we merely pushed this question, this conditional, up to the coordinator? In a way, but we can solve it in a much better way up there.
When we do need to have two flows at once, for A/B testing or multiple size classes, you can just swap the entire coordinator object instead of sticking a bunch of conditionals all over your view controllers.
If you want to understand the way a flow works, that’s now super easy, since all the code is in one place.
View controllers are now reusable.
They don’t assume anything about what context they’ll be presented in, or what their buttons will be used for. They can be used and reused for their good looks, without dragging any logic along with them.
If you’re writing your iPad version of your app, the only thing you need to replace are your coordinators, and you can reuse all the view controllers.
Every task and sub-task in your app now has a dedicated way of being encapsulated.
Even if the task works across multiple view controllers, it’s encapsulated. If your iPad version reuses some of those subtasks but not others, it’s really easy to use just those sub-tasks.
Coordinators separate display-binding from side effects.
You never again have to worry about if a view controller will mess up your data when you present a view controller. It can only read and display, never write or corrupt data. This is a similar concept to command-query separation.
Coordinators are objects fully in your control.
You’re not sitting around waiting for
-viewDidLoad
to get called so you can do work, you’re totally in control of the show. There’s no invisible code in aUIViewController
superclass that is doing some magic that you don’t understand. Instead of being called, you start doing the calling.Flipping this model makes it much easier to understand what’s going on. The behavior of your app is a completely transparent to you, and
UIKit
is now just a library that you call when you want to use it.
The Backchannel SDK uses this pattern to manage all of its view controllers. The app coordinator and auth coordinator examples come from that project.
Ultimately, coordinators are just an organizational pattern. There’s no library you can use for coordinators because they’re so simple. There’s no pod
you can install
and nothing to subclass from. There’s not even really a protocol to conform to. Rather than being a weakness, this is a strength of using a pattern like coordinators: it’s just your code, with no dependencies.
They’ll help make your app and your code more manageable. View controllers will be more reusable, and growing your app will be easier than ever.
There’s an everpresent hilarious thread in programming blogs where an author tries to explain what a monad is, starts strong, and then ends up losing everyone with some nonsense about an endofunctor.
I’m going to take a crack at it. I’m probably going to fail, and I’m going to prove Soroush’s Burrito Law in the process: anyone trying to explain monads is going to fail, even if they account for how hard explaining monads is.
A monad is a wrapper for a thing. (It honestly is like a burrito.)
The Maybe Type
So. Let’s talk Swift. Let’s say you have a function that reads something from disk. It can either return the thing, or it can return nothing. Swift calls this type Optional
, but let’s recreate it and call it Maybe
.
enum Maybe<WrappedType> {
case Something(WrappedType)
case Nothing
}
Angle-bracket blindness is real. WrappedType
here just means that our Maybe
can have anything inside of it, and we can refer to whatever type that thing is with WrappedType
. This lets the compiler know the type of what’s coming out is the same as the type of what’s put in. Let’s continue. Imagine a function called readString()
reads a string from the disk, where its provenance is doubtful. It “maybe” doesn’t exist.
func readString() -> Maybe {
return .Something("someString");
}
let maybeString = readString()
This is the first important part of a monad. You have to have a way to create one. In this case, the constructor, Maybe.Something
, fills that role. In other languages, this is known as unit
or the inconveniently-named function return
. It’s a function that takes one parameter, and returns a monad that wraps that parameter.
If this is all we have, it’s kind of frustrating to use. To get access to the information inside that Maybe
, you have to explicitly unwrap it. The structure of the type forces you to check it, every time.
switch (maybeString) {
case let .Something(definitelyString):
print(definitelyString)
case .Nothing:
print("Nothing!")
}
After we use Maybe
with a case statement like this for a while, we notice that we’re writing this switch
statement over and over. It’s a lot of boilerplate just so we can access the Something
.
Maybe we can wrap up the switch
into a function, and pass that function a block that will be executed if the Maybe is a Something
and not a Nothing
.
extension Maybe {
func ifSomething<NewWrappedType> (block: (WrappedType) -> Maybe<NewWrappedType>) -> Maybe<NewWrappedType> {
switch self {
case let .Something(wrapped):
return block(wrapped)
case .Nothing:
return .Nothing
}
}
}
I would love to remove the types in the function declaration to make it shorter and clearer, but they actually confer a very important piece of information: The block
that we’re accepting takes a WrappedType
(a string, in the example above), and returns a new type wrapped in a monad.
This is very important. ifSomething
doesn’t just give us access to the Something
, it also lets us transform it, but requires that it be wrapped in the Monad type again. (Sometimes we want to transform but not wrap it, and I’ll address that in a moment.)
It’s important that the block returns an already-wrapped monad, so that we can chain these calls. This is a big part of why monads are useful.
Now that we have this power of transforming the wrapped thing, we can do something very cool. Imagine we had a function that converts strings to JSON objects. It returns a Maybe
, since deserializing JSON can fail.
readDataFromDisk().ifSomething({ string in
return convertToJSON(string)
})
or, more succinctly:
readDataFromDisk().ifSomething(convertToJSON);
At the end of .ifSomething(convertToJSON)
, we just get back a new Maybe
monad, on which we can again call ifSomething
. This is how monads let us chain stuff. Imagine another function called getProperty
, which also returns a Maybe
.
readDataFromDisk().ifSomething({ string in
return convertToJSON(string)
}).ifSomething({ JSON in
return getProperty(JSON, "username")
})
Et cetera. We can keep chaining like this, as long as we need to. Notice how this is flat, instead of nested. We’ve totally skipped the part where you have to unwrap an Optional
with an if let
, then unwrap another one, then another one, and you end up with something 5 levels deep.
If we didn’t have ifSomething
, our code would look like this:
var maybeData = readDataFromDisk()
if let data = maybeData {
let maybeJSON = convertToJSON(string)
if let JSON = maybeJSON {
let maybeUsername = getProperty(JSON, "username")
if let username = maybeUsername {
//we can finally use the username
}
}
}
While indented code like this might work in early stages, the fact that it continues to indent inwards means that it’s not scalable. With monads, we can remove the nesting and organize our code much better.
Functional programmers took a great name like ifSomething
and made it totally inscrutable by calling it flatMap
. (In some of the literature, it’s also known as bind
. In Haskell, aka peak inscrutability, it’s invoked with the operator >>=
.)
bind
(or flatMap
) and unit
(the constructor) are all it takes to be considered a monad. From those two, we can also build map
. map
lets us transform the wrapped object without having to rewrap it ourselves at the end of the function. This is particularly useful for arrays and other collection types.
To build map
, we wrap the result of the map
block with the constructor and send that to flatMap
:
extension Maybe {
func map(block: (WrappedType) -> NewWrappedType) -> Maybe {
return flatMap({ wrapped in
return .Something(block(wrapped));
});
}
}
In this way, map can be written in terms of bind
and unit
.
The Monadic Laws
For something to be monad, in addition to implementing bind
and unit
, it has to follow some special rules.
First, left identity.
unit(a).flatMap(f) == f(a)
Wrapping a
in the monad, then calling flatMap
with any function f
will have same result as just calling f
with a
, since f
returns a new monad.
Second, right identity.
m.flatMap(unit) == m
Since the unit
function doesn’t do anything but take the unwrapped value and wrap it, calling flatMap
on an existing monad m
with unit
will have no effect.
The first two monadic laws exist to assert that unit
, the constructor, doesn’t do anything other than wrap a
.
Lastly, associativity.
m.flatMap(f).flatMap(g) == m.flatMap({ a in
return f(a).flatMap(g)
})
This says that we can combine two functions f
and g
into a new function, and calling flatMap
with the new function is the same as calling flatMap
with each of the functions separately.
In Swift
You don’t need to write your own Maybe
type to start using this in Swift today. Swift’s Optional
type supports flatMap
.
let optionalString = Optional.Some("123");
let optionalInt = optionalString.flatMap { string in
return Int(string)
}
Because all versions of flatMap
behave the same, you can use this just like the flatMap
we wrote above, chaining it over and over.
In A Nutshell
That’s monads in a nutshell. Well, they are the nutshell. They wrap a value, like a shell.
Other monads you might have seen include:
Result
, which wraps a “Something” with an optional “Error”.Eventually
/Promise
/Deferred
, which wraps a value that doesn’t exist yet.Array
, which wraps many values.
With Result
in particular, it’s easy to see how you might have a series of functions where each is dependent on the previous one, and where each can fail and generate an error. You would have a pyramid of doom without being able to flatMap
them repeatedly.
I’ve used the Promise
monad a lot in Javascript, and it organizes code greatly:
User.signup = function(user) {
return new Promise(function(resolve, reject) {
User.validate(user).then(function() {
return Bcrypt.genSalt(10);
}).then(function(salt) {
return Bcrypt.hash(user.password, salt, null);
}).then(function(saltedHashedPassword) {
return User.insertIntoDatabase(user, saltedHashedPassword);
}).then(function(userRecord) {
resolve(userRecord);
}).catch(function(error) {
reject(error);
});
});
};
The chaining that the promise monad affords us is crucial when all of your operations are asynchronous and can fail. We return a new Promise in each then
block, and the chain continues. In this way, we’ve described an inherently complex, asynchronous task in a serial list of steps.
Monads are weird thing. The idea lets us treat all these different wrappers, which all serve different functions, similarly. When we know that something is a monad, we gain a ton of knowledge about how we can use it and what it can do.
After all, they’re just monoids in the category of endofunctors.
Two of the most impressive languages in my mind are Lisp and Smalltalk. They are both extremely limited: Lisp is structured around spaces and parentheses. That’s it.
Smalltalk has only 6 keywords: true
, false
, nil
, self
, super
, and thisContext
. Everything in Smalltalk, including conditionals, is handled by sending messages to objects. How many keywords does Swift have already? Why can’t I name my UISwitch
“switch”? (I know why, and it’s vexing.)
Most languages don’t do this. Their users want more toys and more sugar in the language. The language maintainers add it, and you end up with a languages that are a complicated mess.
While most langauges aren’t designed with simplicity as a feature, I’ve found that with a little self-control, it’s possible to intentionally limit yourself to a subset of your favorite language, and have great results.
I’m writing Objective-C and Javascript on a day-to-day basis, so my examples will come from those languages. The idea, however, is universalizable.
First, app structure. When structuring an app, I limit myself to one structure. Inasmuch as I can control it, everything has one and only one owner, so that my object graph looks as much like a tree as possible. From the top, the app is run by coordinators, which boss around the view controllers. Those view controllers each have a view, which is a custom subclass of UIView
designed specifically for each view controller. This lets me push subview configuration and layout out of the view controller and down into the view. Subview allocation is always handled via lazy loading. Layout itself is pushed even further into a layout object.
I’ll break these rules every once in a blue moon, but almost every time, I will write code like this. It helps increase consistency and removes any doubt about how to structure the simple stuff. When a new programmer comes on board, if they understand one section of the app, they’ll be able to easily navigate through the rest of it.
Second, communication patterns. Since everything is a tree, messages need to go down, from parents to children, and up, from children to their parents. Parents know about their children, so they can just send simple messages. But when children talk to parents, I almost always use the delegate pattern exclusively. I know about KVO, I know about NSNotificationCenter
, and I know about the responder chain, but delegates have a simplicity to them that I value. Their relationships are one-to-one (each object generally has one delegate), their data is typed, and it’s easy to search for delegate conformance. There’s a small cost to the extra code of declaring the protocol, but I find that it pays off in the long run.
Delegates are great, but the key here is not the pattern itself, but limiting yourself to it. The artifical limitation simplifies decision-making while writing code. When you limit yourself intentionally, you don’t end with a tangled mess of code that fires an NSNotification
from inside of block that calls another method via a delegate.
Finally, a Javascript example. The project I’ve been working on has a backend written in Express. Express assumes very little about your code’s structure. It gives you the standard Node.js Request
and Response
types, as well as a Router
. That’s pretty much it. Routers let you register functions with routes. Now, I could try to build a roughly object-oriented model layer within Javascript, but I don’t really understand how Javascript constructors work, so that choice is out.
Instead of trying to make each route (represented by a function) talk to an object-oriented model layer, the User
type holds onto all of the things that a user can do.
var loginAction = function(req, res, next) {
var creds = { email: req.body.email, password: req.body.password };
User.login(creds).then(function(session) {
res.sendJSON({ session: session });
}).catch(function(error) {
res.sendError(error, "There was an error logging in.");
});
}
Each of the things on the User
type is a function, and calling it returns a new Promise. This, and the fact that data is represented by dumb objects that do nothing but hold the data, make the model layer is very functional in nature. Of course, it wouldn’t be interesting if it were only in one place, so every single route in the system works like this.
When I’m making a new route, it’s no longer necessary to think about how go from the functional “action” layer to the Promise-based model layer. It’s always the same. The sendJSON
and sendError
functions on Result
are also almost always used, making it dead simple to add new endpoints. You get to the meat of writing your code even quicker. Limiting you and providing a One True Way to do stuff is how frameworks like Rails help you speed up development.
When you limit yourself to a small part of the language, you end up with code that’s simpler and requires less thought to implement. It removes a lot of the weight from working in your codebase and makes it easier for outsiders to understand how things work.
This happens more often than I’d care to admit. While programming, I’ll receive a flash of insight that maps cleanly onto a trite phrase I’d heard dozens of times before. Prefer composition to inheritance. The law of Demeter. Code is read a lot more than it is written. The pith is endless.
You’ll come across a problem and realize how doing it the easy way has bitten you in the ass. You’ll stop making the mistake of doing it the easy way, and start doing it the right way. I’ve found that with lessons like these, you almost always have to go the long way around, experiencing them firsthand before there can be a deep understanding.
A great example of one of these lessons is making small classes and small methods. Ben Orenstein gives advice to this effect in one of his posts. I remember defending to my Ruby-writing coworkers: maybe you could write Ruby that way, but Objective-C is just too complex and verbose to write with one-line methods. I was, of course, super wrong. Not only is it possible to write Objective-C in that style, it’s also joyful.
David Foster Wallace, in his now-classic commencement address for Kenyon College, said:
Because a huge percentage of the stuff that I tend to be automatically certain of is, it turns out, totally wrong and deluded. I have learned this the hard way, as I predict you graduates will, too.
There’s a delightfully meta component here: not only are the lessons hard to learn, but the lesson that the lessons are hard to learn is hard to learn.
There’s a somber component too: learning lessons the hard way sucks. Our knowledge is stuck in our brains, and can only come out in low-bandwidth forms, like prose, speech, or drawing.
When you finally have that bug that causes you to have the epiphany, all you end up with is a shitty platitude. You have no way to impart to anyone else what you’ve discovered. The lesson took time for you to learn, and once you’ve learned it, you wish you could give it to other people. If you’ve ever written a monad tutorial, I’m looking at you.
I’ve found only two ways to route around it. The first is to feel someone else’s suffering at the hands of one of these bugs and experience empathy.
The everpresent joke in computer science says there’s two hard problems: naming and cache invalidation. When I was starting out, I remember thinking “ha ha, there’s no way cache invalidation could be that hard”. Fast forward a few years, and while I’ve learned a few lessons of my own about bad caches, reading Code Climate’s incident report for a major issue made me understand on a far deeper level what it means for cache invalidation to be hard. Lesson learned, bon mot deployed.
The second approach to teaching hard ideas is to change the value function. Programmers take shortcuts and write code “the bad way” because it’s way easier. It feels as though the cost is lower than the hard way, because you can’t see the interest acquired by your technical debt. Instead of making the cost of shortcut higher, we can increase the value of the “right way”.
To make the hard way more appealing to your audience, the trick is finding the perfect example to illustrate it. Most readers will skim a new post when it comes out. They don’t stop and pick out each word you write. Code blocks break up the writing, and they provide somewhere for the readers eye to jump. Given that you’re only getting a small amount of the reader’s attention, you have to find an example that strikes the right note with the reader. The example decides if the post lands or not. It has to be relatable and short, so that you don’t lose the attention of the reader, but it has to be complex enough to not be trivial.
For example, I’m very proud of my post on the method object pattern, Graduation. This post makes it simple, obvious, and elegant to take a method and make a new class out of it. It readjusts the value function in the programmer. When the value of the good way increases, it takes far less activation energy to jump from the bad way to the good way. Put another way, the intensity of the lesson you have to learn is much lower before you’re happy to switch over.
Part of why I love Sandi Metz’s work is that she takes a simple problem, like inheritance, and thinks about it a lot, and finds the right words to make even the most novice programmer understand why to prefer composition to inheritance. I’d heard about this platitude plenty when I was just starting out, but Sandi’s talk was what really drove it home.
Ultimately, we can only do so much. It’s a shame that these methods have to be learned over and over, the slow way. The best we can do is grease that path for those that follow us.
When you first start making an app, your choices about the model layer will impact you the most in the long run. It’ll pay dividends to think about these choices up front, instead of being left with the detritus of accidental decisions.
One of these decisions is how to cache your data: whether you’ll use Core Data or something simpler.
Another big decision is whether each unique model is represented by one instance or many. This is, in some ways, the crux of the difference between object-oriented and functional/immutable styles. More simply put, it’s the class way versus the struct way. Let’s examine the differences.
With the object-oriented style, you have living, breathing models. You can send messages to the model, and it can respond, make decisions, perform network requests, and generally act as a first-class citizen in your app.
Making this work requires a design pattern called the Identity Map, which is just a big dictionary that maps each object instance to its identifier. When fetching a model from a store (whether it’s a network store or a store persisted on the device), each instance is checked against the Identity Map. Objective-C’s flexible initialization makes this really easy.
- (instancetype)initWithObjectID:(id)objectID {
id existingObject = [[SKModel identityMap] objectForKey:objectID];
if (existingObject) {
self = existingObject
return existingObject;
}
self = [super init];
if (!self) return nil;
self.objectID = objectID;
[[SKModel identityMap] setObject:self forKey:objectID];
return self;
}
Core Data does this for you. If you fetch the same object twice (on the same object context), you will get the same instance back. From the Core Data Documentation:
“Core Data ensures that—in a given managed object context—an entry in a persistent store is associated with only one managed object. The technique is known as uniquing. Without uniquing, you might end up with a context maintaining more than one object to represent a given record.”
Given that your stores will return the one instance for each object ID, that model can change out from under any controller-type objects that are holding on to it. Therefore, each controller needs to observe changes on its models and re-render its view to reflect those changes.
There are two cases in which this approach works really well. The first case is when there are many views on screen, some of which point to the same models. Making a change in one view should also be reflected in the other views representing the same object. It also is great with data persisted on-disk. Data on-disk changes frequently and nonatomically. For a todo app, the user might change the due date, which would save the model to disk, then the priority, which would save the model again. Using the same model object makes our program simpler.
The second approach is to use many instances for each individual model. For this approach, each time you fetch from your store (again, either network or persisted), you create a fresh struct (or struct-like object) and use that. When modifying, either ensure isolation of each object by fetching it anew in each place that you’ll need it, or by using copy-on-write to create a new instance for each modification.
This approach shines on single-screen platforms, like iOS, where the user is generally looking at one thing at a time. In cases like this, you can lazily refresh data when it comes back on screen, rather than refreshing it greedily. It also shines in systems where the “source of truth” is on a server. Any mutating REST call is an atomic change that will return a response that is fully-formed and fully-validated by the server application. It’s also great for immutable data, like tweets. When things can’t be edited by the user, it’s much safer to use a system that prefers less mutation, like structs.
While using actual Swift structs grants some guarantees about how the thing will be used, it comes with some cost as well. Drew Crawford writes about the “Structs Philosophy™”.
The insight here is that doing anything of value involves calling at least one mutating function and as soon as you do that, your function must be mutating, and everything that calls you must be mutating, and it’s mutating all the way up, except for the silly leaf nodes like CGRect or your bag of Ints.
Like Drew, I’m not sure I can advise making your whole model layer out of purely Swift structs. For one, any kind of mutation is costly, especially for deeply nested heirarchies of data. Second, structs can’t really represent members like images, data, colors, and URLs as value types yet, even though they are often components of models and clearly are values. Using those types requires bridging to Objective-C, which loses a lot of guarantees of immutability and isolation. Lastly, it requires making your models somewhat “dumb”. While you can attach functions to structs in Swift, they seem to be more for manipulating the data rather than doing any work, like making an API request that regards the model.
The choice between many instances and one is in your hands. Don’t make the decision lightly, however. The path you choose will affect the bedrock of your app, and it will be hard to change later.
There’s a special class of scribe that takes requirements from a client and translates them into highly precise, sometimes arcane documents. Few laypeople can understand these documents and the policies they encode, but they nevertheless have a great effect on everyday life.
The domain of the clients and requirements are often different than as the domain of the scribe; the scribe has to learn to efficiently translate the rules of the business she represents into the precise language of the documents.
When these documents are parsed and analyzed, they’re done by another body. The meaning drawn from the documents won’t necessarily be the same as what the scribe or her clients intended, but the effects of the documents stand, nevertheless.
These scribes are paid a lot of money for their skills, and a lot of people find it frustrating that no one else can do what they do. But ultimately, it’s not really possible have a modern, functioning society without them.
I’m talking about lawyers writing contracts, of course.
The parallels between lawyers and programmers are myriad. These parallels are sometimes trivial, like defining variables; every programmer has seen an employment contract that has text like “Widgets LLC, hereinafter referred to as ‘the Company’”. Defining a variable in this way shortens the contract, prevents redefinition errors, and makes the contract easier to change.
Lawyers also have to flesh out simple ideas (for example, the statement “things that I make”) into really complex and precise statements (“all Inventions that I may solely or jointly author, discover, develop, conceive, or reduce to practice during the period of the Relationship”).
Contracts are more similar to interpreted code rather than compiled code. The contract can be “statically analyzed” (i.e., read by other lawyers) while it’s being written, but its meaning won’t be fully determined until it’s challenged and taken before a judge.
Judges are much more lenient than code interpreters, so typos and other simple errors aren’t commonly held against a lawyer the way they might be with a programmer. Ambiguity, however, provides ammo that opposing counsel can use to change the intent of the contract. That’s a runtime error if I’ve ever heard of one! If only we could give lawyers automated tests.
Much fuss is made about the future of programming, whether it will be text, or some kind of semantic editor, or perhaps something graphical. On the other hand, the future of contracts appears to be a lot more form contracts and contract generation, rather than a dramatic rethinking of their representation. Generated, parameterized contracts parallel either functions or libraries in the programming, although it strains the metaphor a little bit.
It’s also easy to imagine the profession of programming going in the same direction as lawyering. Extreme demand and limited supply has caused engineer salaries to skyrocket to the around (and above, in some cases) a lawyer’s starting salary. The long hours and benefits (free food if you stay past a certain time!) are also slowly starting resemble big law firms. Programming culture could definitely use the specialized trade schools and a the strict pipeline that law benefits from. Our lawyerly brothers and sisters have already been through what we’re going through, and we’ve got a lot to learn from them.
When I write code, my goal is to take as few shortcuts as possible. People often ask me why I bother.
I bother because shortcuts join together like Voltron to tangle your code. I bother because it’s hard enough to read my code when it’s written well. I bother because I never know who’s going to be looking at my code.
Code quality is precisely the proverbial “back of the fence”. Perhaps apocryphally, Steve Jobs was known to care about small, sometimes invisible details. He would fuss over the beauty in a circuit’s design, because his father inspired him to consider the minutiae:
It was important, his father said, to craft the backs of cabinets and fences properly, even though they were hidden. “He loved doing things right. He even cared about the look of the parts you couldn’t see.”
It applies to more than just code and circuit boards. I’ve noticed the best designers that I’ve worked with have meticulously assembled PSD files. Deep hierarchies of organization, consistently-named layers, edges perfectly between pixels rather than on top of them. This attention to detail is reflected in the quality of the designs as well; truly, someone who sweats the details sweats the big picture, too.
The back of the fence never aligns with business metrics; code quality is no exception. It’s a second-order effect which can only affect your bottom line in indirect ways. It’s cheaper to write the code right the first time, rather than having to fix its bugs later. It’s cheaper to work with supple code, code that’s been designed with change in mind.
(There’s one little hack. If you’re working on an open-source project that’ll be used by developers, then your code quality is no longer merely adjacent to cost. You can effortlessly align your business metrics and code.)
In some cases, it’s not possible to draw even the most tenuous connection between the concerns of your business and back-of-the-fence style code quality. For those times, we might call it professional pride.
Joe Cieplinski is a designer who (not by coincidence, I’m sure!) creates extremely neat PSDs. I’ll leave you with some remarks from Joe’s talk at CocoaLove last year:
We don’t design beautiful things hoping that people notice. We design beautiful things knowing that they probably won’t. […] We do design for us. We do design because we want to sleep at night.