📜 ⬆️ ⬇️

Paul Graham: "Revenge of the nerds", part 3

We continue the translation of the essay and the book by Paul Graham "Hackers and Artists."

Recently on Habré flashed an article "We need less powerful programming languages" with "revelations of the 60s." In order to understand this question yourself, you need to understand what “power” is, Graham tells about this at the end of the chapter. Also in the article, Graham raises the question of the clash of interests of tough programmers and mediocre bosses in choosing a programming language. I asked a couple of questions for Edison :

- What do you do if a customer requests to write something in one language, and you recommend another language for the good of the cause?
- We offer the customer the use of a particular language and framework based on the appropriateness for the project. If, for one reason or another, the customer, after listening to our arguments, insists on solving a problem in some other language or platform, then we consider this to be part of the problem statement and solve it that way.
')
- “The ignorant boss” vs “an excellent programmer” - how do you solve this problem?
- This is a question of responsibility. It is the programmer’s duty to think through and convey to the boss the technically best solution, but if the boss for one reason or another insists on his point of view, then a good programmer, like any subordinate, should obey, and not prove his point of view sabotage work. Responsibility for the decision in this case lies with the chief.


“We were chasing C ++ programmers. We managed to drag them a whole bunch halfway to Lisp. ”
Guy Steele, coauthor of the Java specification.

Original - Revenge of the Nerds , May 2002
Thanks for the translation thanks to Shchyokotova Yana.

Start: Paul Graham: "Revenge of the nerds", part 1
Continued: Paul Graham: "Revenge of the nerds", part 2

Part three



Centripetal forces

I do not claim that using non-standard technologies does not require any costs. Not so groundless were the fears of our boss, the profane. But without awareness of all the risks, he is prone to exaggerating them.

I come to mind three problems that may arise when using less popular programming languages. Your programs may not work correctly with programs written in other languages. At your disposal can be much less different libraries. You will also have difficulty hiring programmers.

How scary is each of these problems? The importance of the first varies depending on whether you have control over the entire system. If you create software that will run on a remote user machine that manages Cadillac on a closed operating system (I will not give the name), then perhaps there are advantages in writing your application in the same language in which the OS itself is written . But, if you keep the entire system under control and own the source codes of all its parts, as is probably the case with ITA, then you can use any language you want. If an incompatibility occurs, you can fix it yourself.

In server applications, you can get out of this situation by using advanced technologies, and I think that this is the main reason for what Jonathan Erickson calls the “revival of a programming language”. That is why we learn about new languages ​​such as Perl and Python. We know about these languages ​​not because people use them to write Windows applications, but because people use them on servers. And, since the software is moving from desktops to a server platform (the future, which even Microsoft seems to have accepted), the forced need to use pop technology will eventually disappear.

As for libraries, their importance also depends on the application itself. For less demanding tasks, having libraries can outweigh the true power of the language. So where is this break-even point? It is not easy to articulate, but wherever it is, there will be a shortage of all that is meant by the application. If a company decided to enter the software market, and people are engaged in creating an application that will be positioned as one of its products, then this company will most likely involve several experienced specialists in the work, and it will take at least 6 months to create it. In projects of this magnitude, powerful programming languages ​​are beginning to outweigh the convenience of having libraries.

The third reason for the concern of the authorities, the difficulty in hiring programmers, is a false apprehension. After all, how many professionals do you need to hire? Of course, now we all know that the best software is developed by teams of up to ten people. And you should have no problem hiring employees on such a scale for any programming language, including one that some have hardly heard about. If you can't find ten Lisp programmers, then your company is most likely not located in the wrong city to develop software there.

In fact, choosing a more powerful programming language will slightly reduce the size of the command you need, because (a) if you use a more powerful language, then you probably don’t need so many experts, and (b) programmers working with more advanced ones languages ​​are smarter.

I am not saying that you will not be forced to use what is meant by "standard" technologies. At Viaweb (now Yahoo Store) we caused widespread perplexity among venture capitalists and potential firm buyers by the very fact of using Lisp. But we also surprised everyone by using Intel's universal boxed assemblies as servers instead of “professional-grade” servers from companies such as Sun, using the incomprehensible, open-source version of the Unix-like FreeBSD system instead of real commercial solutions like Windows NT, ignoring the proposed standard commerce SET, which now no one even remembers, and other things.

Managers should not be allowed to make technical decisions for you. Have some of the firm’s potential buyers been alarmed by the fact that we used Lisp? Some yes, slightly. But if we had not worked with Lisp, we would not have been able to create software that would awaken in them the desire to buy us. What seemed anomalous to them was, in fact, cause and effect.

If you are starting a startup, do not design your product in such a way as to please investors or potential buyers. Design your product to appeal to users. If you conquer users, then everything else will follow. And if this does not succeed, then nobody will care about how arbitrary your technological solutions were.

The price of mediocrity

How much do you lose when using a less powerful programming language? On this account there are some data.

The most convenient measure to measure is most likely the size of the code. The goal of high-level languages ​​is to provide you with a greater number of abstractions, larger bricks, so to speak, so that you can do without a huge number of them in the course of building a wall of a certain size. Therefore, the more powerful the language, the shorter the program (not only by the number of characters, of course, but also by the number of individual elements).

How does a more powerful programming language allow you to write shorter programs? One approach that you can use if the language allows is upstream programming. Instead of just writing your application in the base language, you construct a language like yours on top of the base language and then write your program in it. Code assembled in this way can be much shorter than if you wrote your entire program in the base language. In fact, this is exactly how most compression algorithms work. Such a program will be easier to modify, because in most cases, the language layer does not have to be changed at all.

The amount of code is important because time to write a program depends mainly on the length of the code. If your program were three times longer in another programming language, then you would need to write three times as much source code. And it is not possible to cope with this by hiring more people, because on reaching a certain size, additional hiring of labor will turn into a net loss. Fred Brooks described this phenomenon in his famous book The Mythical Man-Month, and everything I saw only confirms his words.

So how much shorter will your programs be if you write them in Lisp? Most often when comparing Lisp with C, for example, there is about a seven to tenfold decrease. But in a recent post about ITA in the New Architect magazine, it’s stated that “one line in Lisp can replace 20 lines with C”, and since this article consists entirely of quotes from the president of ITA, I admit that they learned this number from ITA itself. If so, then this statement can be trusted. ITA applications include a lot of C and C ++ code, as well as Lisp, so they rely on their experience.

I dare to suggest that these numbers are not even constant. I think they grow when you encounter problems more seriously, as well as when more intelligent programmers work for you. From the best tools, the tuning specialist can squeeze more.

As a last resort, if you competed with ITA and decided to write C programs, they could develop software 20 times faster than you. If you spent a whole year on a new feature, they would have managed to duplicate it in less than three weeks. Whereas if they spent only three months developing something new, it would be five years before you could implement it.

And you know what? This is only the best. When we discuss the size of the code, you implicitly admit that you can write programs, oddly enough, in less efficient programming languages. But in reality there are limits to the possibilities of programmers. If you are trying to solve a complex problem with too low-level language, then you will reach the point where there will be very many things that need to be simultaneously kept in your head.

So when I say that an ITA competitor in question would have taken five years to duplicate what ITA could write on Lisp in three weeks, I mean those five years when everything goes according to plan and without a single mistake. In fact, in most companies it works like this: any developed project requiring five years of work may never be completed.

I admit, this is an extreme case. Experts from ITA seem unusually savvy, and C - a rather low-level language. But in a competing market, even a difference of two or three to one would guarantee that you will always be behind.

The surest way

There is some possibility that our ignorant boss will not even want to spend time thinking about all of the above. Like most of them does. And all because, you know, when it comes to this, it is all the same to the ignorant superiors, whether their company will pay for it or not is impossible to prove his guilt. The safest plan for him personally is to stay close to the center of the herd.

In large organizations, to describe this approach, use the phrase "industry best practices". Its goal is to protect the ignorant boss from responsibility: if he decided something in accordance with the "best industry practices", and the company suffered losses, then it can not be blamed for this. He did not make that decision, but the industry.

I believe that this concept was originally invented to describe accounting, etc. Roughly speaking, its meaning is not to do anything strange. And for accounting, this is probably a very good idea. The terms "advanced" and "accounting" do not look very good together. But when you enter this criterion into decisions regarding the technologies used, you begin to receive incorrect answers.

Technologies often have to be advanced. In programming languages, as pointed out by Erann Gat, what actually gives you "best industry practices" is not the best, but only refers to the average level. When a decision forces you to develop software at a pace of more aggressive competitors, then “best practices” is the erroneous use of the term.

So, we have two facts, which I consider very valuable. In fact, I know this from my own experience. First fact: programming languages ​​vary in their capabilities. The second fact: most managers intentionally ignore it. This information is literally considered as the surest way to make money. ITA organization is an example of this method in action. If you want to win in the field of software development, just formulate the most difficult problem you can, use the most powerful programming language you can find, and wait for the ignorant heads of your competitors to lead their firms to fall in price.

Appendix: the power of programming languages

To illustrate what I mean by the comparative power of programming languages, imagine the following problem. We want to write a function that generates accumulating elements — a function that takes the number n and returns a function that uses another number i to return n, incremented by i. (This is incrementing in increments, not just addition. The accumulating parameter should accumulate).

In Common Lisp it will be
(defun foo (n) (lambda (i) (incf ni))) 

and in perl 5
 sub foo { my ($n) = @_; sub {$n += shift} } 


which includes more elements than the Lisp version, because in Perl you have to extract the parameters manually.

Smalltalk code is slightly longer than Lisp
 foo: n |s| s := n. ^[:i| s := s+i. ] 

because, generally, lexical variables work, assignment cannot be applied to the parameter, therefore, it is necessary to create a new variable s.

In the Javascript example, again, a little longer, because Javascript maintains the distinction between operators and expressions, so an explicit return statement is needed to return values:
 function foo(n) { return function (i) { return n += i } } 

(honestly, Perl also retains this distinction, but handles it in a typical Perl manner, allowing you to omit return statements).

If you try to translate Lisp / Perl / Smalltalk / Javascript code to Python, then you will encounter some limitations. Since Python does not support fully lexical variables, you have to create a data structure to store the value of n. And, although in Python there is a functional data type, there is no exact representation for it (unless the body of the function is a single expression), so you need to create a named function to discover. This is what you end up with:
 def foo(n): s = [n] def bar(i): s[0] += i return s[0] return bar 

Python users could rightly ask the question: why can't they just write
 def foo(n): return lambda i: return n += i 

or even
 def foo(n): lambda i: n += i 

And I guess it will happen sometime. (But if they don’t want to wait while Python evolves into Lisp, they can always just ...)

In object-oriented languages, it is possible, to a certain extent, to simulate a closure (a function that refers to variables defined outside the body of this function) by defining a class with one method and a field to replace each variable from the ambient context. This forces the programmer to perform some kind of code analysis, which could be done by the compiler in a language with full support of the lexical analysis context. And it will not work if the same variable is accessed from more than one function, but this is sufficient in simple situations like this.

Python experts seem to agree that the preferred way to solve problems in Python is to write either
 def foo(n): class acc: def __init__(self, s): self.s = s def inc(self, i): self.s += i return self.s return acc(n).inc 

or
 class foo: def __init__(self, n): self.n = n def __call__(self, i): self.n += i return self.n 

I cite these examples because I would not want Python advocates to say later that I had distorted the language, but because both of these examples, it seems to me, are more complicated than the first version of the code. You do the same when you set up a separate place to store the accumulating parameter; it's just a field in the object, instead of the first item in the list. And the use of these special, reserved field names, especially such as __call__, seems a bit rough.

In the rivalry between Perl and Python, the application from Python experts seems to be that Python is a simpler alternative to Perl, but this situation shows that the power of the language is maximally simple: Perl's program is simpler (fewer elements), even if the syntax is a bit wretched.

What about other languages? In the other languages ​​mentioned in this conversation — Fortran, C, C ++, Java, and Visual Basic — it is unclear whether this problem can actually be solved. Ken Anderson says the following code is also close to what you can get in Java:
 public interface Inttoint { public int call(int i); } public static Inttoint foo(final int n) { return new Inttoint() { int s = n; public int call(int i) { s = s + i; return s; }}; } 

This code does not correspond to our description of the task, since it works only with integers. After exchanging a lot of emails with Java experts, I would say that the fact of creating a fully polymorphic version of the code, which works just like the previous examples, is somewhere damn complicated and impossible. If someone wants to write such code, I would be very curious to look at it, but personally I took a break in this matter.

Of course, the fact that you cannot solve this problem in other programming languages ​​is not true in the literal sense. The fact that all these languages ​​are Turing equivalent means that, strictly speaking, you can write any program in any of them. So how would you do it? In this limited case, by writing a Lisp interpreter in a less powerful language.

This sounds like a joke, but it happens so often to one degree or another in large software projects that this phenomenon was called the tenth Greenspan rule: any fairly complex C or Fortran program contains a newly written, unspecified, buggy and slow implementation of half of Common Lisp .

If you try to solve a complex task, the question will be not whether you will use a sufficiently powerful language, but whether you will use a powerful language, (b) write a real interpreter for one such, or ) Will you become a compiler in human form for one of these languages? We can already see how this starts to happen in the Python example, where we mimic the code that the compiler itself would generate to implement the lexical variable.

This practice is not only a general trend, but has also become an institutionalized activity. For example, in the OO world you hear a lot about “templates”. I wonder if these templates are not the embodiment of case (c), about the compiler person, in action. When I see patterns in my programs, I see it as an alarm. The program model should reflect only the problem that it needs to solve. Any other regularity in the code, at least for me, is a sign that I use not powerful enough abstractions, and often that I manually generate the detail of some macro that I need to write.

Remarks

- The IBM 704 processor was about the size of a refrigerator, but much heavier. The processor weighed 3150 pounds (about 1428 kg - approx. Lane), and the 4K RAM was located in a separate box, which additionally weighed another 4000 pounds (about 1814 kg - approx. Lane). One of the largest home-use refrigerators, the Sub-Zero 690, weighed 656 pounds (about 298 kg - approx. Lane).

- Steve Russell also wrote the first (digital) computer game Spacewar in 1962.

“If you want to fool an ignorant boss so that he allows you to write software in Lisp, you can try to tell him that this is just XML.”

- The following is the sum generator in other Lisp dialects:

Scheme: (define (foo n)
(lambda (i) (set! n (+ ni)) n))
Goo: (df foo (n) (op incf n _)))
Arc: (def foo (n) [++ n _])

“The sad story of Eran Gath about JPL's“ best industry practices ”inspired me to use this misused phrase.

- Peter Norvig found out that 16 out of 23 patterns in Design Patterns were “hidden or simpler” in a Lisp implementation.

Thanks to many people who answered my questions about various programming languages ​​and / or read the outline of this article, including Ken Anderson, Trevor Blackwell, Erann Gat, Dan Giffin , Sarah Harlin, Jeremy Hylton, Robert Morris, Peter Norvig, Guy Steele, and Anton van Straaten. None of them is responsible for the opinions expressed here.

Additionally:

Many people responded to this conversation, so I opened an additional page to be able to consider the difficulties that they faced: Re: Revenge of the Nerds .

This article also entailed extensive and often helpful discussions on the LL1 mailing list . See, in particular, the letter by Anton van Straten on semantic compression.

Some of the letters on LL1 prompted me to dig deeper into the field of programming language abilities in Succinctness is Power .

The extended set of canonical implementations of the standard of the accumulative parameter generator is presented on a separate page .

All Graham articles in Russian here .

Source: https://habr.com/ru/post/271103/


All Articles