Learning New Programming Languages by Building on Existing Foundations
How Existing Mental Models Can Help You Ease the Learning Process
Start with Similarities
At their core most programming languages are based on the same set of foundational principles. Implementation varies, but rarely is one language so different from another that parallels can’t be drawn. The key then to learning and understanding a new language is to first identify similarities and then expand on your understanding of a concept if/where necessary. Consider, for example, data types and variables. Every language has a way to define and store data so that it can be used throughout a program. As a result, when you’re learning a new language, figuring out how to define and use variables will likely be one of your first tasks. Let’s look at an example in two different languages: Ruby, which is an interpreted language that uses dynamic typing; and, C#, which is a compiled language that uses static typing.
Imagine that you are an experienced Ruby developer and you want to learn C#. Looking at these snippets, the Ruby one will be very familiar to you. All it does is define a few variables and then print them to the terminal. Now, look at the second snippet. Does it look familiar? The syntax is different but most likely it is apparent that similar action is occurring. An
= operator appears a few times, which likely jumps out at you as a set of assignment operations. Something called
Console.WriteLine() then gets called, which implies that values are being displayed on the terminal. And, there are some strings that appear to be using interpolation to build messages. At the conceptual level, none of this is particularly surprising — assignment, interpolation, and console output is something you are already familiar with from Ruby.
You may not need to know any C# to understand the second snippet, but there is certainly room to expand your mental models. For example, why is everything wrapped in a
Main() method? And what are these
string keywords? Here is where the process of learning begins. You already know the general boundaries of what is happening (assignment, interpolation, output), now it’s time to learn the particulars:
Main(): Some quick research will reveal that the
Main()method is the entry point where program execution occurs. Now you know that you’re going to need a
Main()method in all of your C# programs.
- Variables: The first part of our C# snippet is clearly assignment of some sort. Given the nomenclature, you can probably guess that the
intkeyword indicates a variable containing an integer,
doublea variable containing a double floating point number, and
stringa variable containing a string. Almost immediately, you have an idea that C#, unlike Ruby, must be statically typed since different data types require different variable declaration. Reading the documentation will confirm as much.
Console.WriteLine(): Finally, by running the program you will see that
Console.WriteLine()is outputting values to the console. From Ruby, you know that
putsis a method on the global
$stdoutobject and when you look up the documentation for
Console.WriteLine()you will see that
Consoleis a class in the
WriteLine()is a method defined in that class. Not only is this very similar to
puts, but it also tells you that C#, like Ruby, is object-oriented. Now you have another mental model you can use to draw parallels.
The above is a very simple example, but even this simple example provides some important insights. You have already learned that C# requires a defined entry point, is statically typed (unlike Ruby), and is object-oriented (like Ruby). You learned this first by using existing mental models about things like what variables and methods are, and then you expanded on those models by extending them to include concepts like static typing.
Look for Differences
As you start to examine how to read and write code in a new language, the first step is to see where things are the same, which provides a foundation for you to build on. After that, your next step is to see where things are different. Let’s go back to our Ruby-to-C# learning transition and try something a bit more complicated.
In this Ruby snippet, we define an array called
particles with a few strings in it and then we use
Array#push to add a few more strings and
Array#each to iterate over the array and print each of the strings to the terminal. But can we do the same thing in C#? After some quick Googling you will see that C# has typed arrays (the typing won’t be a surprise to you given what you learned earlier) and there is a method called
SetValue, which sounds a bit like
push but has a value and an index position as parameters. Your first attempt at recreating the Ruby snippet then might look like this:
Unfortunately, this code produces a
Run-time exception when you try to use
SetValue to add a new value onto the array. A look at the documentation will tell you that arrays in C# are not dynamic and must be initialized either with all their values or with a length specification. Your second attempt to replicate the original Ruby code takes this into account and might then look like this:
This snippet does indeed replicate the functionality of the original Ruby snippet but only by the loose definition that they both output the same values to the terminal. If you examine the two snippets more closely, you will quickly see a problem: in the C# snippet you can have a maximum of 5 values in the particles array, whereas in the Ruby snippet you could add as many as you want. It would appear then that arrays in Ruby and C# have a key difference in that the former is dynamic in size and the latter is not. In C#, to truly replicate the Ruby snippet’s functionality, you would need something more like this:
Here, you are using a
List data structure to dynamically gather values together. By doing so, you can use C# to effectively replicate the original Ruby code, but more importantly, you have learned a key difference between the two languages. Although the use of the term “array” in both languages might have implied that they are the same, in actual usage they are quite different. This provides you with another useful point at which to expand your mental model of what an “array” is and how it operates. In C#, an array may or may not be the right data structure to use in places where you would have used one in Ruby, depending on whether dynamic resizing is important to you. Now you know to think about that ahead of time and plan your code accordingly.
Go Back to Core Principles
Exploring new languages by looking for similarities and differences with languages you already know is a great starting point; however, sometimes the best place to start is from language-agnostic principles. Earlier we intuited that C# is an object-oriented language, given that we used a built-in class and one of its methods,
System.Console.WriteLine() to execute an action. We can then reasonably assume that, like other object-oriented languages, C# has a mechanism to define a class and then instantiate objects from it. This is a core principle of object-oriented programming and we can therefore be virtually assured that our assumption is correct. To give ourselves a starting point, let’s see what this might look like in our known language, Ruby.
In this snippet, we have a simple class,
Element, which has a constructor method to accept values and assign them to instantiated objects, a set of accessor methods for getting/setting values, and instance method that outputs those values. The core concepts here are the idea of a class, the idea of a constructor method, the idea of getters/setters, and the idea of an instance method. Going back to our understanding of what an object-oriented language is capable of, let’s see how to do the same thing in C#.
Looking at the C# version of this snippet, we can see that it’s really not so different from the Ruby version. We define a class, use a constructor to define how the class will instantiate objects, define getters/setters, and define an instance method to call on the objects we make. Certainly the two snippets look quite different, but not in a surprising way. The C# version uses
this to refer to the object that is being instantiated where as Ruby used
self. The C# version is typed at both the method and parameter levels, whereas the Ruby one is not. However, at the level of core principles, the two snippets are virtually identical.
To expand on this theme, we might consider the idea of inheritance. We know that inheritance and subclassing are key aspects of object-oriented programming so it’s not a leap to imagine that this as possible in C# just like it is in Ruby.
In our Ruby version, we define a subclass,
NobleGas, which inherits from our
Element class, uses the
super keyword in its constructor to expand on its parent class’ constructor, and then overrides the instance method
describe to define new behavior. We can do the same thing in C#, albeit with different syntax:
At first glance, prior to learning anything about C#, this last snippet might have seemed intimidating. The syntax is unfamiliar, there are strange keywords, and the code is not organized in a way we are used to. However, when you look at the code from a level of core principles, it’s really not so different — it’s just a class definition, a set of methods and variables, and a way to instantiate and use objects.
Learning a new language can be incredibly intimidating if you approach it from scratch. However, most programming languages share a set of foundational principles that we can use to draw parallels, spot important differences, and apply across multiple languages. By examining a new language through your existing mental models, you can look for places where your mental model can remain intact, and for places where it needs updating. As you learn more languages over time, your mental models will have expanded to the point where they need very little updating and can be drawn upon to recognize a wide variety of language implementations.
I hope you have enjoyed this short overview of how to apply existing programming knowledge to your efforts to learn new things. So, next time you’re feeling intimidated by a learning task that is set before you, take time to think back to fundamentals and you will no doubt see that you already know much of what you need to learn.