C++ and Rust are overwhelmed, and programming language design should not be too complicated!

Author | Alan Wu Translator | Meniscus

Produced | CSDN (ID: CSDNnews)

In this article, let’s talk about why we should adopt a minimalist mindset when designing programming languages.

In my opinion, intentional simplicity is highly underrated when designing programming languages. Most modern programming languages ​​are designed to be more maximalist. Adding new features quickly can help increase your competitive advantage. There seems to be a general consensus that if your language doesn’t have feature X, people will choose another language; others think that adding more features is a means of showing progress. This kind of thinking is too simplistic and ignores many other key aspects of programming languages ​​that are necessary for success and progress, such as ease of learning, stability, tooling support, and performance.

change and churn

In my opinion, we should design programming languages ​​with fewer features, and features should not change too quickly over time, which is a powerful feature in itself. Programming languages ​​change frequently, and there are bound to be problems and loss of users. Tools will become outdated, code will need to be updated, libraries will be broken, and users will churn.

I first wrote code in C++ around 1998. I haven’t touched the language in years, and I have to say, I’m at a loss. With so many new features added, C++ feels like a different language altogether. Last year, I wanted to use C++20 modules in a new project, and it turned out that G++ and Clang support was so incomplete that these modules simply wouldn’t work. At the time, my general impression was that the C++ compiler was slow to keep up with the latest developments, probably due to a shortage of developers. The language has become so complex, and so many new features have been added, that compiler developers are a bit overwhelmed. In my opinion, in the long run, C++ will be overwhelmed and overwhelmed by itself.

CSDN paid download from Visual China

Many people forget one thing: for a programming language to be successful, it must be supported by good tools . If the language and its features are constantly changing, the associated tools need to be constantly updated. C++ has many problems, one of which is that the syntax is difficult to parse. This problem emerged as early as 1998. And now the syntax of C++ changes every year or two and becomes more and more complex. What impact do you think this will have? Developers maintaining C++ tools will find other ways, and users of those tools will leave.

Easy to learn and the human factor

Recently, my colleagues and I decided to port our C codebase to Rust. Overall, I’m happy with Rust’s core features, and I think Rust is a huge improvement over C and C++ in many ways. However, in my opinion, one of Rust’s main drawbacks is that it is overly complex . Rust is a very complex language, both syntactically and semantically. Its syntax is very verbose, requires a lot of knowledge, and has a lot of rules and subtleties that make it difficult to figure out what works and what doesn’t. Rust has a steep learning curve and a high cognitive load.

CSDN paid download from Oriental IC

Last week, when I pair programmed with a colleague, he said, “I think the Rust compiler keeps laughing at me for being stupid.” That statement surprised me because I feel the same way. Rust has always been uncomfortable for some reason, and the language’s high level of complexity feels hostile to users. It will break your intuition, as if the compiler is always telling you that the code is wrong. After this conversation with a colleague, I saw an article that also talked about the complexity of Rust, and the description in the article echoed our feelings.

In my opinion, in many ways, programming languages ​​should be kept minimalistic, with as few concepts as possible, and only choose primitives that work well together so that they are easier to learn. The fewer concepts a programming language has, the less knowledge we need to learn and the faster we can master it. Code written in a more minimalistic language is also easier to read. Think about C++ code, the language has so many redundant features that generally we only write code in a subset of C++, and some language features are explicitly forbidden. This may mean that C++ code written by different projects is difficult for others to understand, and the C++ code looks like another language to outsiders.

I feel that, in some ways, minimizing the complexity and number of features of a programming language is a better way to respect programmers. Programmers have busy lives with a lot of work to do, they may not have time to read hundreds of pages of documentation to learn our language, we have to understand them. Programming languages ​​are user interfaces, so the principle of least astonishment should be followed. Minimizing complexity is also a way to reduce cognitive load and respect human limitations. Humans are very capable creatures, but we’re also essentially just smart talking monkeys. We have limited work to remember, limited design constraints we can take into account, and limited time to focus. Despite our human limitations, well-designed programming languages ​​can help us succeed.

At the end of the day, I think the complexity and simplicity of the language affects the ability to attract and retain new users. In my opinion, Python’s creators have done a lot to make learning easier for Python’s initial success and rapid popularity. Frankly, I think the complexity that the upgrade from Python 2 to Python 3 has added to the Python ecosystem, and decisions such as the introduction of the := walrus operator, are disappointing to many.

Minimalism

I’ve mentioned minimalism many times so far, and I’ve also mentioned the principle of least astonishment. For programming languages, minimalism means having a smaller feature set and fewer concepts to learn. Beyond a minimal feature set, however, minimalism also means careful selection of features that fit together seamlessly. If we design a language with a large feature set, there are infinite combinations and interactions between different features, which means that there is a high chance of eventually encountering problems with the interaction of some language features.

CSDN paid download from Visual China

For imperative programming languages, there is usually a difference between the syntax of statements and expressions. Functional programming languages, on the other hand, are structured so that everything is an expression. Therefore, functional programming languages ​​are more minimalistic and less restrictive to the programmer. Some languages ​​distinguish between code at compile time and code at program execution time. This distinction tends to increase the complexity of the language, as parts of the language features tend to be duplicated, and there are limits to the code that the compiler can run at compile time.

The principle of least astonishment states that we want to avoid introducing edge cases that only occur in certain situations. Another pitfall to avoid is introducing hidden behavior that the programmer might not have expected. For example, the equals (==) operator in JavaScript actually includes an implicit conversion to the string type, which means that 1 == “1” evaluates to true. Because of this undesired hidden behavior, JavaScript also has a very strict equals operator (===), which does not perform string conversions. In my opinion, JavaScript should only have a strict equality operator, which should be written explicitly if you want to convert a value to a string before performing an equality comparison.

implementation complexity

Designing a programming language is difficult because the design space of a programming language is infinite, so we have to make compromises. On the other hand, it is difficult to use data to quantify which factors make one design better than another. But some aspects can be quantified to some extent, such as the complexity of a language implementation and how well a particular language implementation performs.

My PhD thesis talked about the implementation of a JIT compiler for JavaScript ES5. Therefore, I am very familiar with the semantics of JavaScript, and I have a good idea of ​​what we need to do behind the scenes in order to make our JavaScript code run faster. But the experience wasn’t pleasant, and I’ve found that a lot of the complexity and hidden behavior in JavaScript, and many other languages, doesn’t do anyone any good.

CSDN paid download from Oriental IC

There is unnecessary complexity in the language, which is bad news for those who learn the language, because it increases the difficulty of learning; bad news for programmers who use the language every day, because it increases their awareness. This is bad news for language implementers and tool maintainers, because it makes their job harder, and it’s even less good news for end users, because This can lead to more bugs in the software and worse performance.

As an example of unnecessary implementation complexity, many object-oriented languages ​​have a concept borrowed from Smalltalk that everything should be an object , including boolean and integer values. At the same time, implementations of these languages ​​have to do a lot of work behind the scenes to represent integers more efficiently while presenting an object-like interface to the user. However, the abstraction of presenting an integer object to the user is often not exactly the same as that of a normal OOP object, which is a leaky abstraction because it doesn’t make sense to be able to redefine an integer value, which must be unique. Also, being able to add properties on integers is a very bad idea and affects performance, so it’s usually not allowed.

At the end of the day, integers are not objects in the OO sense. They are an atomic value with a special meaning. ” Everything should be an object ” is a misconception and doesn’t have any positive meaning in practice. This approach only complicates the implementation of the language and increases the burden on the programmer.

Suggest

While I’ve made a lot of criticism in this article, I’ll try to give some advice as well.

The first piece of advice is that the initial design of a programming language should not be too large . Programming languages ​​are user interfaces, the APIs that people use to interact with machines. The smaller the scope of an API, the less risk of introducing unexpected complexity and subtle design errors.

The second piece of advice is that programming languages ​​should be kept as minimalistic as possible . Fewer features means less duplication and means you can pick and choose features that provide the most expressiveness and value to the programmer. If you want to develop a programming language, do it slowly. Take some time to code in your language and figure out the potential impact of the design changes you make.

It’s easy to add new features slowly, but if you add new features and someone has already started using them, it’s difficult or even impossible to undo them, so choose carefully. Remember, you don’t have to please everyone and accept every feature request. No single language or tool can meet everyone’s needs.

Lastly, just like user interface design. Brainfuck is a very small language with few concepts, but no one would call it expressive or elegant. Lisp is considered by many to be one of the most beautiful and elegant languages ​​in existence, but my mentors were used to naming variables with single letters and very few comments in the code. Elegant languages ​​don’t automatically generate elegant code, but you can encourage everyone to follow good programming practices.

The text and pictures in this article are from CSDN

loading.gif

This article is reprinted from https://www.techug.com/post/c-jiajia-and-rust-are-overwhelmed-don-t-make-programming-language-design-too-complicated0371df90115ce3367e67/
This site is for inclusion only, and the copyright belongs to the original author.

Leave a Comment

Your email address will not be published.