My understanding of programming languages

In this article, I explain my understanding of programming languages. It includes what makes a language good in my perspective, the key to learning a programming language, and my understanding of design patterns.

Here I use Kotlin language code for the example, but there are several programming languages, including Rust, that I like.

The essence of programming

In my opinion, the essence of programming is to express ideas in a programming language. For example, if you want to implement an app similar to Twitter, you may imagine a text box in the program with a “publish” button. When the user clicks this button, the tweet is published by being stored. Then it can be displayed to the viewer. Of course, the actual implementation will be much more complicated, including how the client and server interact, and how data is stored. But when implementing these, we also turn ideas (what protocol to use, what database to use, etc.) into code.

For another example, you want to implement a sorting algorithm. You may be thinking about playing poker. Every time you grab a new card, you insert it into a specific position, so that the card sequence is ordered. This way, you end up with a sorted sequence of cards. The process of programming is to express this idea in a programming language, so you can sort in the program.

What makes a programming language good

In my opinion, a good programming language is expressive. I can express my idea in code by only “verbatim translation”. The code just differs from what I think in terms of words and syntax. And when I see the code in the future, I can quickly know what I was thinking at the time.

As an example, I want to express “User information”. It consists of a username, a nickname, and an optional birthday. The username and nickname are a piece of text, and the birthday is a date. In Kotlin, I can express it using the following code.

1
2
3
4
5
data class UserInfo(
val userName: String,
val nickName: String,
val birthday: Date?,
)

The code is only different from my idea in terms of words. The code uses data class to represent data, String to represent text, and ? to represent the optional. However, the code is a visual representation of my thoughts. No structure difference, no missing information, and no extra code.

If I want to construct a user whose username is Jason5Lee, whose nickname is Jason Lee, and whose birthday is empty, I can use the following code in Kotlin.

1
2
3
4
5
UserInfo(
userName = "Jason5Lee",
nickName = "Jason Lee",
birthday = null,
)

It's also a visual representation of what I'm thinking. In other programming languages, either sacrifice this intuition (for example, use new UserInfo("Jason5Lee", "Jason Lee", null), I can't just write which is which, but rewrite them using a certain order), or use the Builder Pattern that requires writing extra code that doesn't exist in my mind.

Simplicity v.s. Expressiveness

Of course, there is no such programming language that is completely intuitive to reflect my ideas, a programming language needs to balance expressive and simple. Programming languages should not be overcomplicated to cover all possible logic. Now, however, people put too much emphasis on simplicity and ignore what I think is more important to being expressive.

Firstly, some complexity exists objectively. By pretending not seeing it only makes it worse, because you still need to express it, but in an unexpressive way. Ignorance is NOT strength.

Secondly, if the total amount of code you write in a language is , and the time spent is , do you want to be smaller or to be smaller? The simplicity of a programming language affects more while being expressive decreases .

Advantage of programming languages

In addition to being executable by the computer, an important advantage of a programming language is its precision. A programming language has very little ambiguity. I can concisely express my idea in a programming language, and anyone who knows the language can understand it precisely. You may feel programming language is weird at first. But for an expressive programming language, once you get used to it (which takes a constant time), you can express and read ideas more efficiently (which saves time for every code you write).

For the statically-typing language, I can even spot potential mistakes in our ideas. Using an expressive statically-typing language, I can have my idea, write them as code, and get feedback on the mistake from the IDE/editor live checks. Then I can just execute the code I write.

How to learn a programming language

Since the essence of programming is to express ideas in a programming language, in my opinion, the key to learning a programming language is to learn how to express. Each programming feature is a tool to express a certain idea. For example, the if is to express executing different code based on the condition, similar to the “if” in our mind. The inheritance in OOP is to express an “is-a” relationship. If a class is a specific kind of another class, you can use inheritance to express it. I will write more articles about how I understand the programming language features and how I use them to express myself. You can have your understanding too.

The Design Patterns

When such an idea is hard to be expressed directly in a language, we may create a certain code pattern to express it. This is how we have design patterns. In my opinion, design pattern exists due to lack of a feature, and lots of people blame the wrong thing for the design pattern. Because design patterns are initially called “Elements of Reusable Object-Oriented Software”, It is easy to mistakenly think that we don't need them as long as we don't do object-oriented. But in fact, design patterns exist because it is hard to express some ideas with only object-oriented programming features. A design pattern may be necessary even in a non-object-oriented programming language if these ideas are still hard to be expressed directly.  Correspondingly, a design pattern may not be necessary for an object-oriented programming language if the use case of the pattern is covered by a language feature.

For example, the Visitor pattern expresses that data has multiple cases. Each case may have different content. The processing of data may have different processing based on the case. The data has stable cases and their contents, while the processing of it is unknown and extensible. The Visitor pattern expresses by defining a visitor interface, including the processing of each case. Then, the data provides a visit method that accepts a visitor and performs the corresponding processing based on its case.

If the Visitor pattern is unnecessary in a programming language, it must have a feature to express this data. In Kotlin and Scala, the sealed feature can, and in Rust, the enum feature can. So in these three languages, we can say Visitor pattern is unnecessary. In a language that does not have this kind of feature, The Visitor pattern is necessary then, despite whether it is “OOP” or not.

When you are learning design patterns, you should focus on what it tries to express.

Special Thanks

My programming understanding is heavily influenced by Scott Wlaschin. He has an F# blog and a book about how Domain Modeling matches Functional programming.

I form my understanding by generalizing the domain modeling into a general idea-expressing, with my view about OOP and design patterns. Even though I have some other preferences over F# because of the different views, he takes an important role in the forming of my understanding, and I'm grateful to him.