I have been recently been looped in rethinking the curriculum for my University’s computer science degree. The current curriculum is strongly based on learning the fundamentals. A trait that allowed it to stay mostly relevant even 25 years after its introduction. Still, a quarter century in our field is a lot of time and revisiting the curriculum was due.
A very common question, when building or rebooting a curriculum in computer science, is how to introduce students into programming. What language should we start with? Should it be a simple bare-bones language or a feature-rich one? Should we use a real language or one that was designed to be easily taught and learnt? What paradigms should this first language favor?
In rethinking the curriculum, I was brought back to rethinking my own personal experience during those first University years, and the impact that curriculum design had on them as well as my professional career.
Leveling The Field
When I was pursuing my computer science degree fifteen years ago, my university introduced us to programming by learning Haskell. I know it may not seem the most intuitive option at first, but it turns out the experience was absolutely fantastic.
To begin with, and this may just be my personal opinion: Haskell is a lovely language.
Now, on to more objective territory: Haskell is a simple language. I typically equate the simplicity of something with the number of times that I find myself saying “except” or “but” whenever I explain it. Haskell has very few rules. They may not be the most intuitive rules but they are indeed very few, and they are consistent: no exceptions. Haskell’s syntax is elegant and clean.
Haskell is also a pure language. A “pure programming language” is one that is designed to enable development in one paradigm to its full extent. In the case of Haskell, it is a pure functional programming language. Typically languages fail to reach this level of purity. Indeed most mainstream programming languages are not pure, as they sacrifice this characteristic for other, more practical trade-offs. I am not arguing that a pure language is indeed better than an impure one as a general statement. But for the specific case of learning something, a pure language is indeed preferable.
Finally, Haskell was a fair language to our class. I mean fair in the sense of justice. Many of us had some experience with imperative programming before entering the university. However, most people did not. Had we started learning an imperative programming language like Pascal or even C this would have been unfair. Now, functional programming… that was something new for the entirety of the class.
I personally had had exposure to programming during high school: Logo, Pascal, C, Visual Basic and ASP. By the time I started college I was pretty sure that the first programming courses in University would be a breeze. Well… nothing quite far from the truth. I was soon faced with the stone cold fact that all my programming experience was in the realm of the imperative paradigm. I remember realizing how my apparent advantage dissolved into thin air right in front of me as soon as I started learning Haskell. New paradigm, new rules. All my experience was of zero use to me.
The Theory Of (Linguistic) Relativity
So let us take a closer look. I already had 3 or 4 years of programming experience and I learnt about 5 languages by that time. None of that counted. Why?
In the realm of natural languages there is a concept called linguistic relativity, also known as the Sapir–Whorf hypothesis. It states that the structure of a language affects its speakers’ worldview.
A practical example of Sapir–Whorf in practice is how sexist vocabulary can influence career choices in a society. The terms “firemen” or “policemen” may not sound the most inviting to a woman as a career choice. While our language certainly affects our worldview, the inverse can also occur: our worldview can in turn affect our language. It is more common nowadays to use terms such as “firefighter” or “police officer” in an effort to remove gender biases.
Would this apply to programming languages too? In my case, it certainly did: my experience in imperative languages forged my worldview of what “programming” ment. When a language like Haskell was presented to me that was completely outside of my understanding. It seemed like it was not even programming… it looked more like math! My mind was super conflicted: “Was everything I learnt so far still valid? What is this new thing? What is going on?”
Functions Here, Functions There, Functions Everywhere
In time I came to love functional programming and I believe that it is one of the most influential trends in modern programming language design:
- Scala and Groovy are, without a doubt, two programming languages that revitalized the Java ecosystem. They were introduced in the early 2000s. Around that time Java was in version 5.0, generics where “the new thing” and Lambdas would not be around for another decade. Scala and Groovy could not be more different from each other; one is compiled the other is interpreted. One has an “academic” touch, the other one is pragmatic as it can be. However, both of these languages target the JVM and both of these incorporated functional programming elements that would then inform the Java language itself when these were incorporated in its version 8.
- Similarly, in the .NET community, C# incorporated the concept of lambdas well before Java. Furthermore, F# is an OCaml-inspired language which makes functional programming readily available to any .NET project.
So after learning Haskell and then using functional programming concepts extensively in other languages, my worldview of programming has certainly broadened. It used to be that imperative style was programming and that functional style was kind of like math. Now imperative programming looks kind of cumbersome to me and functional programming looks clean and stylish. Another example of linguistic relativity in practice.
Embracing Multiple Paradigms
I see paradigms as lenses that help us understand a problem under a different light. While I focused on functional programming here, there are other paradigms that are worth briefly discussing.
Logic programming is an interesting paradigm in which we state facts and rules about how a certain domain works and we ask whether certain other facts are derivable from those. It is a good lens to use whenever I need to think about a problem that involves multiple solutions.
Contrary to object-oriented programming, event-oriented programming focuses on state transitions instead of state itself. I find it to be a great tool in highly dynamic scenarios in which the mutation of state is the key aspect to model.
Finally, paradigms do not just apply to programming languages. As a devops practitioner I have seen a shift from bare metal to virtualization to containers to serverless. As an architect I have seen a shift from monolithic to microservices and back. As a leader in my organization I have seen a shift from waterfall to Scrum to Kanban to a mixture of those that made sense for us. While trends come and go, I find it key to keep an open mind and always try to apply the best tool for the job at hand.