This is Words and Buttons Online — a collection of interactive #tutorials, #demos, and #quizzes about #mathematics, #algorithms and #programming.
Quite a few programming languages were already invented in the 21st century. Swift, Kotlin, and Go are probably among the most popular. However, the distinct feature of the 21st-century language design is the absence of any distinct features in the languages themselves. The best thing about any of these is that you can spend a weekend and claim to learn a shiny new thing without actually learning anything new. They don’t have anything new in them at all; they are all made by the “something done right” formula, this something being Objective-C, Java or C.
While “not being new” is indeed a valuable trait in its own right, the question arises. Are they indeed the languages for the 21st century, or are they merely the reflection on the 20th-century bad programming habits?
If I were to invent a language, I wouldn’t try to fix the past. I would try to invent a thing that not only works well in the reality of the modern world but can also evolve properly and stand the test of time. If this requires radical design decisions, so be it.
Modern languages syntaxes reflect the freedom of chalk and blackboard put into the shackles of ASCII. While some elements of notation like arithmetical signs and brackets are more or less idiomatic, some are just made up for no reason at all apart from saving the effort of pressing teletype buttons.
Typing is not an issue anymore. We are not obliged to play guess with our syntax. Things like this (($:@(<#[), (=#[), $:@(>#[)) ({~ ?@#)) ^: (1<#) are indeed concise and expressive. And also fun to write. *
But they do not help readability and, what’s even more critical, googleability and stackoverflowability.
The same goes for cryptic function names, return code conventions, and attributes with obscure meaning. They served us well in the past, saving our punch-card space, now they deserve retirement.
Ultimately this:
FILE * test_file = fopen(“/tmp/test.txt”, “w+”); |
Should become something like this:
create file /tmp/test.txt for input and output as test_file |
We don’t need all that brackets, quotes, asterisks, and semicolons (unless they really help us to express things better). Syntax highlighting should work instead of syntax notation just fine.
Things that are cheap in the 21st century: parsing time, computer memory, online search. Things that are not: development time, programmer’s memory, effort spent online learning the language specifics. This type of writing should facilitate the usage of cheaper things over the more expensive ones.
You probably know this as one of the JavaScript wats.
> 10.8 / 100 0.10800000000000001 |
It isn’t, of course, JavaScript specific. In fact, it is not a wat at all, it is a perfectly correct behavior backed by the well-respected IEEE 754 standard. It’s just how floating-point numbers are implemented in almost any architecture. And it’s actually not that bad considering we are trying to squeeze an infinite amount of real numbers into 32, 64 or even 256 bits.
What mathematicians consider impossible, engineers do by trading off sanity for possibility. IEEE floating-point numbers are not, in fact, numbers at all. Maths requires real numbers' addition to have associativity. Floats and doubles do not always hold this property. Maths requires real numbers to include all the integers. This is not true even for the same sized float and uint32_t. Maths requires real numbers to have a zero element. Well, at least in this regard IEEE standard exceed the expectations, as floating-point numbers have two zero elements instead of one.
And it’s not only about floating-point numbers. Native integers are not much better. Do you know what happens when you add up two 16-bit numbers like that?
0xFFFF + 0x0001
|
Well, nobody knows actually. Intuition tells, that the overflowed number should be 0x0000. But this is not specified by any worldwide standard; it’s just how it usually goes with C and with x86-family processors. It may also result in 0xFFFF, or trigger an interrupt, or store some special bit in a special place signaling that the overflow happened.
It is not specified at all. It differs. While floating-point numbers are just standard insane, these are entirely unpredictable.
What I would propose for numeric computations instead is fixed point arbitrary sized data types with standard defined behavior on underflow, overflow, and precision loss. Something like this:
1.000 / 3.000 = 0.333 0001 + 9999 = overflowed 9999 0.001 / 2 = underflowed 0 |
Of course, you don’t have to actually write all the trailing zeros, they should be implied by the data type definition. But you should be able to select your maximum and minimum bounds for the type yourself, not just rely on the current processor's architecture.
Wouldn’t it work much slower then? Yes, it would. But realistically, how often do you have to program high-performance computations? I suppose, unless you work in a narrow field of research and engineering that requires exactly that, not very often. And if you do, you have to use specialized hardware and compilers anyway. I’ll just presume, a typical 21st-century programmer don't have to solve differential equations very often.
That being said, shouldn’t fast, complex and unpredictable native types from the past be an option and not the default?
There are brilliant wonderful languages designed not to do the task, but to create languages that do the task. Racket, Rebol, and Forth to name a few. I love them all, they are a pure delight to play with. But as you might guess, being fun is not exactly what makes a language universally popular.
Language leverage, the ability to create new sub-languages for the task, is a great power, and it pays vastly to have it when you work in research all on your own. Unfortunately, if you write code for other people to understand, you have to teach them your language along with the code. And that’s when it gets ugly.
People are generally interested in getting things done, not learning the language they’d have to forget anyway after the things are done. For other people, learning your language is just an effort that would hardly pay off. Learning something common and standardized, however, is an investment for life. Therefore, people will rather reinvent your language themselves than learn it. And there you go: countless dialects for the single domain; people arguing about aesthetics, ideology, architecture and all the things that are irrelevant; million lines of code being written just to be forgotten in months.
Lisp guys went through all of that in the 80s. They figured out that the more of the practical part of a language is standardized — the better. And they came up with Common Lisp.
And it’s huge. The INCITS 226–1994 standard consists of 1153 pages. This was only beaten by C++ ISO/IEC 14882:2011 standard with 1338 pages some 17 years after. C++ has to drag a bag of heritage though, it was not always that big. Common Lisp was created huge from the scratch.
A programming language should not be that huge. Not at all. It’s just that it should have a decent standard library filled with all the goodies so people wouldn't have to reinvent them.
It is difficult to balance hugeness and applicability. We had to learn this with C++ the hard way. I think, to balance things properly, the language for the 21st century should be more domain specific than not. Since business applications are currently the biggest mess, perhaps it should address that and not some game development or web-design.
The language for the 21st century should be business oriented, English-like and not dependent on native types.
The most exciting thing, we already have the language exactly like this! What do you think it is?
Index #programming | ← there's more. |
+ Github & RSS |