The Better Parts: Douglas Crockford talk on JavaScript and future programming languages from .concat () 2015 conference
Who knows more about JS than one of his "fathers"? On HolyJS 2017 Piter will come the legendary Douglas Crockford, the creator of JSON and the author of a variety of JavaScript tools. On the eve of his speeches in St. Petersburg, we publish a translation of his speeches in .concat () 2015: The Better Parts - about how to use existing programming languages more efficiently and what will be the programming language of the future. Although more than a year has passed since the speech, the report touched upon a number of “eternal” questions of programming, which we are sure will be relevant in 3-5 years.
The path to perfection
This man - the famous aviator and writer Antoine de Saint-Exupery. Most remember him for the fact that he is the author of the wonderful children's book “The Little Prince” (although this book is not quite a child’s book). In addition to her, he wrote many other books, and in one of them there is an amazing phrase: "As you can see, perfection is achieved not when there is nothing to add, but when nothing can be taken away."
This is a brilliant quote. It was used in conversations about design, architecture, attracted to everything that combines creativity and discipline. ')
He talked about the design of aircraft. But it seems that the idea is actually wider. I think that it is best suited to the computer program. Because we have a special relationship with perfection that is missing from other disciplines: what we write must be perfect, or it will behave incorrectly. And the author gives us some insight into how we achieve perfection through subtraction.
I think this also applies to programming languages.
Programming languages tend to be progressively more complex. But if we want to achieve perfection, it is necessary to remove some things from there. This is the principle of strengths. According to this principle, if a function is useful in some cases, and dangerous in others, there is a better option, always use the best option.
This is a surprisingly contradictory statement. There are many people who do not want to use the best option. And this happens because of a misunderstanding of one thing: we are not paid for using each function of the language. At the end of the project, there is no portfolio manager checking: “Did you use double equality?” No one cares about that. We are paid to write programs that work well and are error free.
When did “no mistakes” become part of the deal? It has always been part of it. We just rarely achieve this. It is easy to forget that in fact the absence of errors was the first requirement. Therefore, a good programming language should teach you this.
I encourage people to learn as many programming languages as possible, because each of them will give you new ideas that you can apply to other languages.
The language that taught me the most is JavaScript. I had a lot of time to deal with it, because I made every mistake that could be made in JavaScript. And I started from the very first - the worst - I did not bother to learn a language before I began to write in it.
In the end I learned the language, and he taught me. And he continues to teach me. I used this language to write a tool called JSLint, which reads JavaScript programs and tells you how to make them better. And JSLint taught me even more. All this has changed my point of view on programming, so now my main goal is to try to create programs that do not contain errors. And JSLint gave me a lot of information on how to do this.
I wrote a book about my experience using JavaScript and JSLint code - “JavaScript - Strengths”. You may have heard of her. It is still the best seller, which is rare for software books. Most software books become obsolete even before they are released, but this one is still relevant. That's because the "strengths" are still strengths. Well, the language has not changed at all.
Counter Arguments
Now the arguments against the use of "strengths". I would like to present them for you:
The first is: “ what is good and what is bad is just a matter of opinion ”. It is not true. As part of JSLint support, I get error messages from people from all over the world. I recently received a letter from one company that spent two weeks trying to solve a certain problem. It turned out that in one place a dot was added before the equal sign. No one saw her because she looked right, but the result was bad. So they asked me if I could modify JSLint so that no one else would suffer from a similar problem. Why not? I have been doing this for many years. So if you use JSLint, you will never waste two weeks on this kind of problem. And this is not an opinion, this is a fact.
Each function is an important tool . It is not true. You can write the best programs without using some of these functions. And if you can write better programs without them, then they are not that important.
I have the right to use every function . The topic of conversation has changed. Do we no longer talk about how best to write programs. Are we talking about our rights? On this argument, ultimately, ends. "I have the right to write shit," right? It does not matter. It is important that we are obliged to write very well.
I need freedom of expression or "I am an artist and I express myself by putting a semicolon at the beginning of the operator, not at the end." The considerations are the same.
I need to reduce the number of keystrokes . We assume that we print most of the time. And if we could find a way to link programs with a few keystrokes, that would make us much more efficient. But the situation is exactly the opposite. The main time waster is not a set of characters, but a glance into the abyss with a cry: "my god, what I did, why it does not work." This is where we spend most of the time. If I could offer you a scheme whereby increasing the number of keystrokes by a factor of 10 could cut errors twice, this would be a huge victory (unfortunately, I do not have such a scheme).
"It is an insult to assume that I will ever make a mistake using a dangerous function . " “I know that you can be mistaken, but I am so skilled that it saves me. To think differently is an insult to me, a personal injury. ” It's all clear.
" There is a reason why these features were added to the language ." This is absolutely wrong. There are many reasons why things fall into the language, but most of them are not compelling.
For example, JavaScript was developed and implemented in just 10 days. It's amazing. And the brilliant man who did this, Brendan Ike, made several mistakes in those 10 days. One of them is a double equality operator, which he forced to do a type conversion before comparing elements. This causes false positives that are confusing. Brendan Ike admitted that the operator was not implemented correctly. At that time, many other languages (for example, PHP) had the same error. Brendan wanted to fix this. Therefore, when work began on the standard, he decided that now was the time to make corrections. He went to ECMA and said: "This behavior is wrong, let's do it right." But ECMA refused, although they offered a compromise: to introduce a triple equality operator that will work correctly. At the same time, double equality remains in the language for people who have already begun to use the wrong design. As a result, we have an operator without good reason.
Brendan calls these features a “shot in the foot” ( footgun - literally translated as a shotgun, - ed. ). And he inadvertently put them in javascript. Their use is not recommended.
So, the purpose of programming languages is to help developers create programs that do not contain errors.
We used to think that writing good JavaScript programs was simply impossible, because it was such a fragile language. But it turns out that writing good JavaScript programs is not only impossible, but not necessary. Just because of its fragility, JavaScript requires more discipline than any other programming language. And you really have to stick to it in order to write something that will work.
There are two things that work against it. First, it is a fantasy of infallibility. In particular, young programmers believe that their skills are so advanced that they can do amazing things that will work. Secondly, there is the futility of impeccability, which you see especially with old guys who have been doing things that have never worked for years. These two very different views lead to one and the same: danger driven development. And that's bad.
One of the things that complicates the management of software development is the complexity of planning. There are two times you should be aware of. Time A is the time it takes to write code. We really do not appreciate it - we do not have a science that would answer the question of how to estimate time A. But the situation is even worse with the estimation of time B - the time required to make the code work correctly.
Time B should be 0, right? You write code - it should work. But time B sometimes becomes more time A. Sometimes it is infinite. This happens when you have a software project that ends, but then it is abandoned before it starts working.
Everything you do within time A, which increases time B, is wrong. You should try to reduce time B to zero.
New strengths of ES6
There are a number of additions to JavaScript that will be sent to the ECMA General Assembly in June of this year ( these innovations really went through a certification procedure and became part of the new specification - ECMAScript 2015 Language Specification, - ed. ).
I am glad to announce that there are some new strengths. And today I would like to share them with you.
Tail recursion
The first and the best is a construction called tail recursion. If the last thing the function does is return the result of a function call (which may be a call to another function), instead of creating a reverse call sequence, the compiler generates a jump. So the code will work a little faster, which is nice. But even better, for some models it will significantly reduce memory consumption. Thus, a completely new class of algorithms can be used.
For example, we can use the continuation transfer style and other types of programming that we could not implement in the old language. So with this function, JavaScript finally becomes a real functional programming language, and that's great.
Dots
We also get the dot operator. If we put it in a list of parameters or arguments, we can deal with a variable number of arguments, which is really nice.
Here we have two versions of the curry function. The first is in accordance with ES6:
I will not explain what exactly is happening in the second version, because everything is just awful. The first one, on the contrary, looks quite reasonable: everywhere we see points (here you can have as many arguments as you like). And that's great.
Innovation does not allow us to do what we could not do before. But when you have to deal with a variable number of arguments, this approach is much more pleasant.
Modules
Now we have modules. You can import and export values from different files; and it finally has support in the language. Previously, all this was implemented through global variables, and it was terrible.
Now we can do it right. If you don’t feel like it’s great, try implementing the same thing in accordance with the previous standard. But with ES6, we can finally do it right in a fully asynchronous language, without blocking.
Constants
We have two new ways to define variables: let and const, which solves the problem of block scope. It turns out that in a good program you do not need the scope of the block. But JavaScript syntax previously looked as if the block had a scope (with its var syntactic construct), although it was not so confusing to people. Every time due to confusion there were errors. Let and Const allow this to be avoided.
Again, new designs do not allow writing any programs that we could not write before. But now the code does not confuse Java programmers, which is good.
Destructuring
We have a destructuring, which is another syntactic candy. It also does not allow you to do what you could not do before. But for some things, the new syntax is much more expressive.
For example, here I have an object. And I want to create some variables by initializing these variables from the properties of the object.
let {that, other} = some_object; let that = some_object.that, other = some_object.other;
This is a very simplified example. Later I will show you another example of how you could use this.
Weakmaps
We had a WeakMap. WeakMap works the way objects should work. In JavaScript objects, keys are strings, which is an error. It would be better if some values were used instead. But, nevertheless, these are strings. WeakMap solves this problem. Here you can take any value and use it as a key.
Unfortunately, we had to add this thing, and it greatly complicated the language. In addition, we called it the worst name ever used in programming languages ( weak - weak, Eng. ). No one wants to put something weak in their program. But this design works really well.
With WeakMap, you can write programs that could not be created in the language previously.
Pattern lines
And finally, we are something that I called the megastroc literal (in the language this is called patterned strings, but I don’t like this name; but previously they were called quasi-literals, which was even more confusing).
These are regular expressions that correspond to multiple lines in ES6. Announcement of this regular expression is just awful; I hope someday we will do something better. But in ES6 we have no better alternative.
So, we have a function that takes a string and converts it into a regular expression, after removing all spaces. Having such a function, I can take any expression, but not write everything in a heap, but put in it the necessary empty space to see the elements and how they relate to each other. The result will be the same.
This construction has one drawback - the compiler treats the regular expression as a string, so it cannot perform the check. Validation will not occur until we call the constructor. But in the previous version of the record due to the fact that everything was mixed in a heap, serious errors could also easily slip past the compiler. Therefore, I do not think that we will lose much in this transformation.
By the way, if you often work with regular expressions, I highly recommend a tool called RegulEx. Put a regular expression in it, and it will build its diagram so that you can see exactly what is going on inside. I use it every time I write regular expressions.
Arrow functions
Another new feature we got from ES6 is anonymous switch functions. A good motivation for their appearance was that some people complained about the large amount of text when typing the names of functions. So I added such a thing. This is essentially a shorter designation for a function. As a result, I get a function that will take an argument and return an object whose specified property has this value.
Everything is good, except that it does not work properly. If you call this function, it returns undefined instead of a value, because there is an error here. So this is another of those marginal things that are sometimes good and sometimes not. It would be great if it worked all the time, but it’s not.
Disadvantages of ES6
In addition to strengths, ES6 has frankly bad elements.
Classes
The worst innovation is the classes. The class was the most requested new feature, with most requests coming from Java programmers who now have to write in JavaScript and they are really outraged by this. I want to write in Java, but the tasks and money are in JavaScript, so they have to do it. They do not want to relearn, so the new syntax will simplify their lives.
All anything, but relying on this new syntax, they will never understand how the language works. And they will never understand how to use this language effectively, although they will think that they understand. They will continue to write, not knowing how unhappy they are. It is a trap.
Object.Create
I am revising what was written in Strengths, taking into account new knowledge and the need to reflect the innovations of ES6. When I wrote “strengths”, I recommended using object.create instead of classes.
In fact, I was the one who managed to add object.create to the language so that I could use it. It turned out well. But how surprised I was when I noticed that I had stopped using object.create. I added it there for myself, but even I do not use it. And the reason is that I stopped using this.
This
I stopped using this due to the fact that in 2007 I made a project called ADSafe. At that time there were several research groups, such as fbjs on Facebook, Caja Google, Web Sandbox on Microsoft, besides, there was my own project - ADSafe and others. We all tried to figure out how to make JavaScript a safe language, so that we could add third-party code to the application and be sure that it would not cause any security problems. And in JavaScript it turned out to be a very difficult task.
One of the things that makes it difficult is this. If you have this in a method, it is bound to the list of object events (this is good and necessary). But if you call the same method as a function, this is bound to a global object, which completely violates our security. How to handle it?
Most projects dealt with this problem with a compiler that translates JavaScript into JavaScript with many runtime checks and interactions to prevent security issues.
My approach at ADSafe was much simpler. I just made it illegal, prompting you to abandon the programs where it is used. It works. The only problem is that you have to give up the lion's share of the programs, because everyone used this.
However, my hypothesis was that if you remove this from the language, we still remain with a functional programming language, which is enough to write good programs. To test it, I started writing also (without this) in JavaScript, free from restrictions. And I was very surprised to find that I began to write better programs with less effort, without having this. Very cute. Needless to say, if the presence of this in a language in itself causes difficulties: speaking in English, I cannot know in advance whether the construction of a language or a pronoun is meant. It is quite difficult.
Null / undefined
I stopped using null. JavaScript has two minimum values — null and undefined. In some languages, it is believed that you and one should not be. JavaScript is the only language that has more than one such value. But it is absolutely clear that it is silly to have two at once.
Some frameworks attempt to treat them interchangeably, but they are not. Depending on the situation, I have to use only one of them. I used undefined because it is the language itself that uses it: you get undefined if, for example, you refer to a missing property.
Indefinite meaning gives rise to a whole range of problems: a completely wrong type of missing object. We had hoped that this would be fixed in ES6, but this did not happen. Probably, it will always be so. But if you do not use null, then you will not encounter this problem. And I stopped using erroneous values, deciding that their very existence in JavaScript was a bad idea.
Initially, this idea came from C, which uses 0 to represent no, false, and other similar values. JavaScript tried to do the same. But it is confusing.
For
I stopped using for. In ES5, we introduced a new set of methods for working with arrays - for each, map and others, so now I use these tools. If I have an array and I need to process it, I use one of these methods. And I can combine them together in a convenient way. It is really expressive.
For each
I do not use for each. In ES5, we got object .keys (object). This construct gives you a nice array of strings. And it does not include things from the prototype chain, so you don’t need to filter the results. It's nice that I can take an array and execute for each in this way, this is a really expressive way.
I mentioned earlier that the correct tail recursion appeared in ES6. And now it's time to stop using cycles at all (in particular, the while operator) - it's time to use only recursive functions.
For example, this is a recursive function. You call this function until it returns undefined. The first version of the code is written using a while loop.
functionrepeat(func) { while (func() !== undefined) { } }
The second version is written using tail recursion.
In ES6, these two methods should work at the same speed, consuming exactly the same amount of memory. Thus, cycles have no advantage over recursion. It's great.
Next generation language
I thought a lot about the next generation programming language. What will it be? It should probably be a language with which we will replace JavaScript, because it would be very sad if it turned out that JavaScript is the last programming language. It was unbearable. We should make the world a better place, at least for our children, right?
I was thinking about how this language will be. What properties should it have? What problems will it solve? How do we recognize it when it does appear?
In one thing I am sure: when it finally appears, we will reject it. And the reason for this is that programmers are emotional and irrational abnormal people.
We believe that this is not the case. We think that we are ultra-rational because we play the role of ambassadors of people in the world of computers, and computers are completely rational. Accordingly, we assume that they themselves are also rational. Many of us have lost social skills, but it turns out that the irrationality and emotionality has not gone away. Let's look at the evidence in support of this thesis:
It took a generation to recognize high level languages as a good idea. When Fortran appeared among many assembler languages, the developers (at that time they were all assembler programmers) refused to switch to it. Because Fortran robbed them of the control of interaction with the machine, which they could not allow. The transition would have made their lives much better, but they abandoned it.
It took a whole generation to accept that go to was a bad idea. For decades, we have had emotional debates about whether we should use go to or not. Both deeply thought-out arguments from very intelligent people were cited, as well as statements in the style: “This is my way of self-expression”, “I need efficiency”, “I will die, but I will not give it away”. It turned out that all these arguments were completely wrong.
It took a generation to agree that the objects were a good idea. In 1967, a language called Simula appeared in Norway. As far as I can tell, only one person in the world - Alana Kay from the University of Utah (USA) - acknowledged that this language contained important ideas. Everyone else missed it. He thought that the objects that appeared in Simula were so expressive that he could develop a programming language for children. And children could write amazing programs using this paradigm. Therefore, he began to learn this language and spent almost ten years developing and improving his own. In 1980 it was finally published. And it was the best developed programming language in history - C ++. Alan Kay took Simula's ideas, did not quite understand them, but translated to C.
So, the industry has a choice: we could go after Simula or C ++. The final decision was made by people who fundamentally did not understand what the objects were for programming. But they made a choice in favor of C ++, because there you didn’t need to understand object-oriented programming to get started with the language. And since then, almost all languages borrowed more from C ++ than from Simula. And so we are still wrong.
Finally, it took two generations to agree that the lambda was a good idea. Lambdas appeared in a language called Squeak at MIT in the early seventies. And the industry did not pay attention to it at all. It took not even two, but four generations to finally get the lambda to the mainstream. It took so long that some people thought this was proof of the insolvency of the idea. But it turned out that functional programming with lambbds is very effective when working with asynchrony and distributed systems that we deal with today. The first language to adopt this idea is JavaScript. After that, lambdas appeared in Python and Ruby, and, ultimately, C #. Not so long ago finally in java. But JavaScript was the first.
The reason why everything happens for so long is that we cannot change the mind. We have to wait for the generation to retire or die before we can get a critical mass of users of a new good idea and continue to work with it.
I remember when go to came. The argument around him did not subside for years, and then suddenly it became quiet. Can we get rid of him now? Yes, we already just threw it. Does anyone suffer from a lack of go to? All disputes turned out to be meaningless, there was a paradigm shift.
It is really difficult for people to move this paradigm. The history with go to is just getting rid of one construction and changing the way a program is structured. It turned out that the lack of go to facilitates programming.
That is why I believe that the next programming language will be rejected first.
About the classification of languages
Think about how we classify programming languages. I divide languages into two main types: system and application.
System languages are used to write memory allocators, system cores, device drivers — these are very low-level languages. Everything else must be written in application languages.
Perhaps the biggest problem with Java is that its creators could not decide on which side of this division they wanted to be. And so Java is trying to ride both tasks.
We need new languages in both categories. For example, the dominant system language today is C, which appeared in the sixties. But my work is focused on applied languages - most of us will deal with them.
Applied languages can also be divided into two groups: the classical school (which includes almost all languages) and the group of languages for prototype programming, which includes JavaScript.
And I think JavaScript chose the right direction. Causing a lot of criticism, JavaScript contains innovation.
When you program in the language of classical school, designing a system, you need to make a classification of objects. It is necessary to analyze all the objects in your system in order to understand what they consist of, to conduct a taxonomy, to find out how the classes will be connected with each other, how and what will be implemented. All this is quite difficult, since it is most often done at the beginning of the project, i.e. at the point where you have a minimal understanding of how the system should work. Therefore, invariably you get the wrong taxonomy. And so you end up with incorrectly selected objects. And from now on, you fight them. All this does not allow to solve the problem correctly. You want to have multiple inheritance, but all this simply does not work. , , , , , , . . , , , . , ( ).
- . . - , . JavaScript, ( ), . .
Java-. , . — , , .
. . , , .. . , , 1995 . But not today. . . , — . - .
, , . , . , . , . , . , .
. JavaScript , . , . , .
, , - . , — JavaScript .
, . . , . .
{ let a; { let b; ... a … ... b … } ... a ... }
JavaScript , — , . .
functiongreen() { let a; functionyellow() { let b; ... a … ... b … } ... a ... }
, , , ( — ).
, , ? . , . , A?
, , . — , heap, . .
, , ES6. , constructor:
functionconstructor(spec) { let {member} = spec, {other} = other_constructor(spec), method = function () { // member, other, method }; return Object.freeze({ method, other }); }