Does a certain language force you to think in a certain way?
For inspiration, see what psycholinguists say in regards to natural language.
In natural language, it is no longer believed your mother tongue constrains your thoughts in any way. On the contrary, it may simply force you to fill in details other languages would leave implied. Some languages would force you to use geocentric coordinates, for instance, so that you described everything in terms of north, south, east and west rather than ‘in front of you’ or ‘to the right of you’. Other languages require you to fill in details regarding how you learned a certain fact, meaning that the tense differs whether you were inferring a thing or knew it to be true. Still another, more popular language (some might know as English) require you to put every event in time using future, past or present tense, while not all languages (like Chinese) are bound by this rule.
I think there is a high correspondence between what we know of natural language and computer languages. Hence, we can ask a similar question here – does the choice of language constrain you to think in a certain way? Or more weakly, does a choice of language require details other languages do not? Our answer will be strikingly similar to the one found for natural languages.
First, no language forces you to think in any certain way. This may be eye-opening for those who have learned a procedural language and then went on to learn a functional one. They might claim that the procedural language forced them to think in terms of steps, while functional languages opened their minds to decomposing problems in other ways. This is absolutely true. One must realize, though, that you can write functional code in a procedural language. It’s a way of thinking, and you can absolutely express things generally ‘native’ to one language in another as you see fit. A more crude saying that exemplifies this point is that “You can write Fortran in any language.”
What we see is not that any language constrains you to think in a certain way, but because of what languages require of you, if you are not exposed to other languages, it is easiest to think in your mother language’s preferred way.
Let’s analyze C++ for example. Similar to the way French requires every object be a he or a she, C++ requires us to specify how we pass all of our arguments in our functions, whether by reference, pointer or value. These things are implied by other languages, yet C++ forces you to think about it. In C++, this turns into a lot of extra specification, but gives us the benefit of faster code if used wisely, while in languages like Python, we have to jump through extra hoops to ensure we pass arguments around efficiently.
Similarly, C++ requires us to tell the compiler whether we want to allocate a variable on the stack or on the heap, a detail that is not required of things like garbage collected languages. This means that a programmer very familiar with C++ will always think of things like object life cycles and the performance and safety benefits of stack versus heap allocations, while a programmer more familiar with C# will not take these details as seriously (even though C# uses stack and heap allocation too).
On the flip side, in English, we can suppose a gender for every object we like, and we have the ability to speak in terms of geocentric rather than egocentric coordinates. Likewise, all Turing complete languages can accomplish any goal, it’s just a matter of how verbosely they do so and how ‘natural’ it appears. Because details like how an argument is passed and where it is allocated are not as important in a language like Python, it is less natural to optimize a Python program based on those things (although entirely possible). Conversely, because of the requirements of an intuitive understanding of the cardinal directions is required to speak in terms of geocentric coordinates provides a speaker of a geocentric language an uncanny ability to know where they are on the planet, the requirements of thinking about allocation schemes in C++ tends to leave the C++ programmer much more finely tuned to the issues of memory safety and resource allocation.
A keen example of this would be in the RAII idiom found in C++ versus other resource release schemes. Memory, after all, is only one resource that must be managed, so when it comes to safely releasing files, locks and other resources in the face of exceptions in other languages, some tooling is required. Compare the try/catch/finally block to the RAII scheme. Many C++ programmers, including this one, would say that an RAII scheme (or handle/body scheme) is cleaner, reads simpler, and is safer than a try/catch/finally system. Why does this cleaner result occur? Because C++ programmers are used to always thinking about resource allocation/free’ing, while other languages this concern is more foreign.
Just so you know this isn’t a C++ fest, consider functional programming. Many functional programmers have pointed out that a few of our cherished Object Oriented ‘patterns’ are just functional programming in disguise, with “Strategy” being a good example. In a pure OO language, or an OO style of C++, strategy patterns require a bit of up front documentation and boilerplate, while in languages where functional idioms are expected, no such pomp and circumstance is required. Is the strategy pattern or lambda functions a better way to fulfill this need? Considering that like try/catch/finally blocks and RAII, both can express the other and are completely equivalent, there’s no mathematical or computational reason to prefer one over the other. However, I think most people who are familiar with both approaches would agree that simple lambda functions are a cleaner way to solve problems the Strategy pattern could solve.
The take away here is that, yes, certain languages prefer you think in certain ways. But problems are best solved in a myriad of ways. The best way to learn about these different problem solving techniques is actually not to solve them in your mother tongue, but rather solve them in a cleaner tongue and figure out how that solution translates. We should not be teaching OO programmers the Strategy pattern, for instance, but rather teaching them higher level functional programming – then showing how the strategy pattern can fill this role in a purely OO language or design. Similarly, while thinking of the gender of an object can lead to some insight into design and understanding of it, it’s boilerplate for the most part. Better to leave the gender ambiguous or non-existent, but be keenly aware of gender-requiring languages so that the concept is not entirely foreign to you.
Another deeper understanding to take away from this may be that the languages that are the ‘best’ are those that allow us to specify the most, but require us to specify the least. Imagine a form of English that can be written tense-less, yet one can add flow of time information as needed as an example.
No comments yet.