If you were to ask me to summarize software engineering in one word, “tradeoffs” is the one I’d pick. We make tradeoffs every day: we choose one language over another based on some requirement or constraint, we opt for fast and cheap even though we know sacrificing quality means we’re incurring technical debt, we select a technology that scales to the extent we need at the cost of a steeper learning curve or implementation overhead.
We don’t necessarily consider learning a new language to be a lesson in tradeoffs, but I think the second programming language you learn is exactly that. A programming language fundamentally represents how an engineer or group of engineers thinks about solving problems, and since solving problems is all about tradeoffs, it’s impossible for you to learn a language without coming to grips (consciously or not) with the tradeoffs its creators made (consciously or not).
If you ask your friend who writes Haskell what a monad is (as you eventually must), you might discover that a monad is a monoid in the category of endofunctors, or a programmable semicolon, or a burrito, or some similarly unhelpful combination of words. While any of these might be true, the fact is that none of them tells you what a monad is for. (If you’re curious, this post by James Coglan is the best explanation of what monads are for that I’ve ever read.)
ref) straight; I tried memorizing their definitions and reading documentation, but to no avail. Finally, one day, I made myself go through a couple of examples in The Joy of Clojure. Chapter 11 covers mutation, and in particular goes through the “ideal cases” for each state management primitive.
As I worked through the examples, I discovered that atoms were great for managing independent stateful events (which I later used when swapping a default image for a broken
<img /> tag), refs were ideal for coordinating multiple events via transactions (think of transferring money from one bank account to another), and agents were best suited to asynchronous and I/O operations (say, firing an ad beacon). The kicker was that all this was clear as day when I went back and read the blog posts and documentation that had seemed impenetrable to me before, but I couldn’t wrap my head around it until I stopped caring about what things were and focused on what they were for. And once I understood that, I also began to understand the tradeoffs between these tools: the benefits and drawbacks of using each.
Learning a second programming language is useful for all sorts of reasons, but I think the main benefit is this: via an understanding of what things are for (rather than simply what they are), you discover why a language’s inventors and users do things the way they do, and in reaching that understanding, you gain a deeper understanding of the tradeoffs involved.