Hey Readers!
We’re now at 130+ subscribers, a doubling of last week! 🎉 Welcome to everyone new 👏, and thank you all for your support!
Housekeeping
Before we start with today’s topic, let’s touch upon the reader survey. 17 responses so far (Results in next section)
If you have a minute, fill it out 👉https://forms.gle/Y7g8gJYyvGuzScQ3A It helps me tailor my writing to your interests and makes this newsletter better.
Also, come find me on Twitter. Some of you already have ❤️
That 1 like is me :)
Survey Highlights
Exhibit 1: Professional Experience
This is a great distribution for this newsletter - exactly what I was imagining!
Exhibit 2: Topics
“How to write good code” was number one!! Other topics of interest (over 70% consensus):
How to work effectively on a team to ship good products
How to do good work, but also look good doing it
How to develop a good relationship with your manager
Databases, Queues, and Caches (and other useful infrastructure)
Startups and business topics
In future posts, we’ll be covering a lot of the soft skills that will contribute to your success, but today we’ll pick up another technical topic - a little piece of advice that could improve your code quality.
And it starts with a Reddit post.
A “cry” for help
Last week on Reddit, I saw this question:
I couldn’t resist answering. I explained Optionals
, linked a video (jokingly suggesting the author should try YouTube first), and finally, linked this newsletter 😆.
While choosing a topic for the week, I realized that Optionals
would be the perfect.
What is an Optional?
First things first, in this post, we’ll refer to Optional
in a Java context. But they exist in many languages, and go by other names: Option
and Maybe
. Different names, same thing.
According to Java docs, an Optional is:
A container object which may or may not contain a non-null value. If a value is present,
isPresent()
will returntrue
andget()
will return the value.
If you know what an Optional
is, that’s a great definition. But for someone learning Optionals
, that definition is wack.
(This is an extremely common pattern in this industry - reference materials generally don’t link to educational materials. Those who have knowledge often create materials for knowledgeable peers, forgetting the newcomer. The haves and the have nots stay separate. Sigh, I digress…)
In my opinion, the easiest way to conceptualize Optionals
is to compare them to Lists
.
Lists
are in every language and they have a few special methods. In this post, we’re focusing on two:
map
- returns a new list, after applying a mapping function on all elementsflatMap
- just likemap
, but if the result of the mapping function is another list, then the resulting list of lists gets flattened. (It’s okay if this is confusing - flatMap takes a few examples to click)
Here’s a Javascript example:
let list = [1, 2, 3];
let result = list // starts [1, 2, 3]
.map(x => x * 2) // yields [2, 4, 6]
.flatMap(x => [x, x]) // yields [2, 2, 4, 4, 6, 6]
Now back to Optional
.
Optionals
are a lot like Lists
. But unlike Lists
, they only contain zero or one item.
Like Lists
, they also have map
and flatMap
.
map
- returns a new Optional, with the contents altered by a mapping functionflatMap
- just likemap
, but if the result of the mapping function is another Optional, then remove one layer of Optional. (This prevents us from having an “onion” of Optionals: an Optional, which contains an Optional, which might contain a value)
Here’s a Java example:
Optional<String> op = Optional.of("yo"); // create an Optional
Optional<String> op2 = op.map(str -> str.repeat(2));
op2.get(); // get the contents, which is "yoyo"
Optional<String> op3 = op2.flatMap(str -> Optional.of(str.repeat(2)));
op3.get(); // "yoyoyoyo"
That’s a contrived (useless) example, but Optionals have a few benefits.
They hint to the reader that the value might be missing
They offer a series of safe and elegant methods for dealing with a value that might be missing
In practice, this makes it obvious to a reader of your code that a value might be missing and reduces the chances of NullPointerExceptions (NPE).
In a production system, issues that arise from improper handling of null are frustrating, expensive, and common (speaking from experience). Anything that reduces their likelihood is a valuable tool.
The Extended Family
Optionals
are nice, but not groundbreaking. They might improve your code a little (clearer and safer), but I wouldn’t write a post just on Optionals
.
You might have noticed, I made a big deal about Optionals
being like Lists
. And the reason for this is that Optionals
and Lists
belong to a larger family of data structures that have similar characteristics.
I like to call this family of data structures “container types”. The simple idea is that you can take a value, really any value, and wrap it in one of these containers. Let’s take an integer like 42:
You can wrap it in an Optional (Java):
Optional.of(42)
You can wrap it in a List:
[42]
You can wrap it in a Promise (JS):
Promise.resolve(42)
You can wrap it in a CompletableFuture (Java):
CompletableFuture.completedFuture(42)
You can wrap it in an Observable (JS):
rxjs.of(42)
You may never have heard of a Promise
or a CompletableFuture
or an Observable
, but because they’re members of the same family, you can probably guess two methods that are available to them.
If you guessed map
and flatMap
, you’re right! Now, they’re not always called map
and flatMap
:
Promises combines
.map()
and.flatMap()
into .then()
- that is to say.then()
performs the responsibilities of bothCompletableFutures uses
.thenApply()
for.map()
, and.thenCompose()
for.flatMap()
But the concepts will be there. Recognizing that these are simply containers, will mean that you can expect to find map
and flatMap
. When I first encountered the (verbosely named) CompletableFuture
, I searched for these two methods and was able to onboard to a project in a day. When I first encountered Observables
, I did the same thing - and this cut my onboarding drastically.
“Container types” are a general and useful concept. So who do we have to thank for them?
Functional Programming
The world of Functional Programming (FP) is filled with jargon, symbols, and incomprehensible explanations. It’s a subculture of programming that seems to attract mathematicians, academics, and purists.
Our “container types” actually come from this subculture, where they’re called “Monads”. (In truth, some of our containers may not obey the formal requirements of being Monads, but this formal classification is as useful as wondering if a hotdog is a sandwich. No cares if they’re not - the similarities are obvious and useful enough.)
In addition to Monads, functional programming brings a lot of other terrific ideas to the table:
Immutability - reduces the surface area for bugs
No side effects - increases code reuse, improves the logical flow of your code
Declarative style - results in less, more readable code
Incorporating these principles into your code will drastically improve your quality. Pure functions FTW!
That said, when taken to their extreme, functional practices are not very pragmatic. There’s a reason Haskell hasn’t caught on (see: joy and agony of using Haskell in Production). It’s hard to be strict about all of these principles all the time. Not to mention that it’s hard to hire talent.
But I know many engineers who roll their eyes at functional programming, and I think that’s a shame. There is so much to be inspired by from the world of FP.
It’s why I recommend all engineers to take the first course in Coursera’s Scala specialization, a 6 week sampling of FP by the man who created Scala: https://www.coursera.org/learn/progfun1
But you don’t have to! It’s optional 😉
Conclusion
Well, that wraps it up. Learning a little bit of functional programming will do you a world of good, and I hope you’re somewhat convinced.
This idea of blending academic insights with pragmatic decision making is a motif in this profession. Stripe created an API that was inspired by the principles of REST, but they didn’t adopt every academic requirement (sorry Roy Fielding, no one cares about HATEOAS). Google started off with the PageRank algorithm, but has since adopted a combination of real world heuristics and signals to deliver useful results.
It’s my belief that a lot of pragmatism, with a healthy serving of theory and academic purity - can go a long way. So study theory, but not too much, and go out there and build great software. 🏗
Till next time! Stay safe 😷
Phil
Very informative, thank you! The combination of academic and practical discipline seems to be the way to go, but it can be difficult to not see things in a "this way" or "that way" perspective.
Great read!