In my recent op-ed on clean coding in Swift focused on Type Inference, I began by saying,
Quick! Tell me! What is the Type of the birdDetails constant in this code example:
1 let birdDetails = birdDetailsFromStorage()
With no additional context to glean information from, the correct answer to the question is, “I have absolutely no clue…”
But is that concluding assertion true? Hmm…
I’m learning, and as I’ve weighed a recent Twitter conversation and thought on a comment thread that Rob Napier made on the post quoted above, I’m compelled to expand a little on my first post on Type Inference as it relates to clean code in Swift.
Something struck me as I read Rob’s comment: Why wouldn’t I know the Type that would be inferred from what is returned by birdDetailsFromStorage() and assigned to birdDetails? Presumably, I named the function what I named it intentionally. The part I missed, was that if I had designed well and created a Type called BirdDetails (say, a Struct as Rob proposed), then all of a sudden, an inference can be made by both the compiler and a human that the birdDetails constant is… well… an instance of BirdDetails. Imagine that!
To quote Rob (emphasis added):
Type inference should only be used when the result is unambiguous, but the best solution is to make the result unambiguous.
Bingo. The best solution is to make the result unambiguous. You and I as code authors are in charge of the clarity or ambiguity of our code – it’s up to us to make the results of our function evaluations unambiguous.
When I named the function birdDetailsFromStorage(), I heavily implied something about its return Type in the name. I even implied it in the name of the constant. I was expecting the return Type to be something that encapsulated whatever “bird details” are – I just didn’t realize it at the time (although it’s super obvious now)!
The very name of a thing sets expectations for you and the readers of your code. It’s our job to set ourselves up for that expectation to be fulfilled! Be predictable with the return Type of your functions for your own sake. A function signature should be such that when you run across birdDetailsFromStorage() in some piece of code, you are able to legitimately expect it to return a BirdDetails.
Using this predictable, convention-based approach to give humans the right clues about what the compiler is going to compute a Type to be makes Type Inference a totally legitimate language feature to embrace for the sake of your code’s clarity and simplicity.
I’ll leave you with a final quote from Rob’s comments:
Don’t go implying that you’re returning one thing when you’re returning another. If you must, then yes, explicit types are your punishment. 😀
Thank you Rob for your insight and feedback. You can read more of Rob’s work at http://robnapier.net.