It needs to be repeated over and over that Common Lisp is a pretty damn poor choice for this blogpost's title, even though the first part of its name kinda-sorta fits into the "tragedy of the commons" idea.
CL honestly isn't a large language by modern standards. The Common Lisp standard library is tiny compared to modern C++, Java, Python, Ruby, Rust, Haskell, APL, or whatever other mainstream language one is working with nowadays. CL might have been "batteries-included" in 1994, when it was standardized, but the idea of "batteries" that a language can and "should" include has been expanded hundreds of times since then.
Smalltalk, Lisp, Forth and retro computing are just easy front page HN submissions.
I understand why people might get nostalgic for simpler times and simpler computers, I'm partial to the rose coloured glasses view of the past myself.
Its been interesting using C# for the last 15 years through that perspective. Microsoft seem intent on folding every cool language feature from every programming language into .NET and it does feel overwhelming, but in the end most experienced programmers have a style so we naturally settle on a subset of language features until we need something new. I really wish somebody had implemented the async patterns in mainstream languages in the 1990s for example.
I really don't think nostalgia for simpler times is useful for solving todays problems, I much prefer C# to Smalltalk or lisp for getting stuff done today.
It’s hard to argue with c# as a pragmatic choice today:
The language itself has adopted just about everything interesting from contemporary languages, from pattern matching to top level statements allowing you to write a hello world or simple scripts that rival python for simplicity.
It’s performant, asp.net is at the top of the tech empower benchmarks, c# is only just behind c++ on the compiler benchamrks game leaderboards (although if you actually read the code i question the value of the deceptive impls there, e.g. the regex redux example is just calling a c library rather than using native c# facilities).
It’s not as memory efficient as go, by about 5x-10x worse but it’s comparable to java. The clr has a much faster allocator than go (although go developers just pre-allocate up front as an idiomatic optimisation).
Since dotnet 5.0 the developer experience is very decent across all platforms. 6.0 with its further enhanced hot reload etc looks even better.
It’s cross platform - apple tv apps or apple watch, ios, android, windows, linux, macos, they’re all first class citizens.
Whether you want to write 3d games (ubiquity), machine learning, web apps, desktop apps, mobile apps, wasm / blazor, it has 1st class support for them all.
And yet, i can’t help but feel common lisp is probably a better language.
The developer experience in CL is superior - repl driven development combined with structural editing and easily accessible ast manipulation just put CL (and other lisps) beyond what more static languages can ever hope to offer in terms of developer ergonomics.
And very slightly nicer syntax in clojure, at least to my eye (i'm talking about the vector arg list vs list, and i believe the use of a keyword as a function to pull a value from a map is a clojure-ism too.
Both could be added to common lisp though - the vector arg list would be a short reader-macro and my lisp-foo isn't yet strong enough to say how to implement the keyword as a function thing but i'm 100% confident it can be done...)
You have to like the JVM, though, because all the errors are pretty much straight Java stacktraces -- very little encapsulation of any of that. So Clojure effectively means you have to know Java/JVM and Clojure.
"It’s performant, asp.net is at the top of the tech empower benchmarks, c# is only just behind c++ on the compiler benchamrks game leaderboards". That part isn't available with Clojure.
Lisp probably is a better language, the pragmatism is what keeps me on C# for server systems. Ironically, now that the desktop gui trend is over, there is no better time for Lisp to make a comeback.
The CL 1994 standard has close to 1400 pages; the C++11 standard had the same size and doubled the number of pages compared to C++03. In comparison to that, the original Oberon language specification in 1990 had less than 20 pages. CL might be "small" in terms of syntax; but the number of constructs to take into account in practice is still large.
Also the C++ standard includes the standard library. The standard library can be considered part of the language. If you include the standard library for C# this adds up to several thousand pages. Lisp like Smalltalk have very little syntax, but this is mostly because constructs which are part of the syntax in bigger languages are constructed in Lisp and Smalltalk on the simple syntax; and you still have to know how to write loops or closures or the like; or if you want OO in Lisp you have to know CLOS, which has many pages.
The Common Lisp standard library includes most of the control flow constructs, the object system, and ways to bind values. Things that in another language would have been in the core language. You can't learn CL in any meaningful sense without in-depth knowledge of the standard library.
It is true that the CL standard is not much smaller than the C+11 standard but the number of pages on my (officially bought) standard copies are:
Common Lisp 1994 (2004 edition): 1153 pages
C 1999: 554 pages
C++ 1998: 776 pages
C++ 2011: 1356 pages
C++ 2014: 1375 pages
C++ 2017: 1618 pages
C++ 2017(2018): 1620 pages
In comparison, the reference manual for Ada 1983 had only 336 pages and the Algol 68 report had only 146 pages, these 2 being considered very large languages at their time.
It is true, however, that the Algol 68 report would have required a double length to include enough explanations to make it intelligible for everybody.
I referred to the ANSI CL draft standard; the PDF has 1356 pages; the syntax section has 42 pages, but for CL code of real world applications at least 800 of the remaining pages are relevant.
I also have the even earlier "Preliminary Ada Reference Manual", from June 1979, which has only 150 pages.
At that stage, Ada had already won the DoD competition, but until it was published as a military standard a few more features were added and some sections of the specification were rewritten, for increased precision.
In any case, the June 1979 Ada version already included what I consider as the most useful Ada innovation, even if it is just a small detail: allowing underscores inside numbers, for better readability.
It always annoys me when I have to use some programming language or software tool that happens to miss this feature, even now, more than 40 years after it became known that this is a good idea.
Underscores inside numbers! I had no idea. Yes, it's a very good idea. I bet someone out there hates it though. It's in Python, Javascript, and Java at least now... what others?
While C++ 2014 added a digit separator, in my opinion using a single quote instead of an underscore as in Ada and in all the other languages following Ada was an extremely stupid decision.
The rationale for this surprising choice was that a number starting with an underscore could be interpreted as an identifier.
??!! Why would anyone want to start a number with an underscore? Underscores are needed only inside long numbers.
The syntax should have allowed underscores only inside numbers and there would have been no problems with backward compatibility, as such numbers would have been illegal before.
The single quote cannot have been easier for parsing as the new use must not be confused by the parser with character literals, which can appear in the same context.
The underscore was first introduced in a programming language by IBM PL/I (in 1964, when it was still named NPL) with the precise purpose of improving the readability of identifiers.
Ada has extended this use to also improve the readability of long numbers.
Why C++ has chosen a different character, which is normally used for completely different purposes, without having any decent justification for such a baffling decision, I cannot understand.
Since D popularized it, many other languages added it. Even C is finally doing it (although using a ' instead of _, which I do not understand the rationale for).
You must include POSIX as part of C, as it is hardly used without it, and also remember that Common Lisp and C++ standards include the standard library.
Had you added Java, .NET or Python, with their standard libraries, C++ would look like a toy language.
I don't buy the basic premise that CL is too large. Languages get popular if they allow programmers to achieve what they want to achieve with minimum effort. It doesn't matter whether they are large or small. CL never had many good GUI options (except very expensive commercial implementations) and until recently many programmers wanted to create GUI applications. So CL was a bad choice. It's still a big minus. CL lacked a good package manager, which was very inconvenient. Now that there is Quicklisp, a big plus. CL has no concurrency support in the language and the quasi-standard Bordeaux-threads has disadvantages over concurrency primitives of other languages.
Here you go, three reasons why CL is less popular than it could be that have nothing to do with the size of the language, and these are just examples. A language doesn't need to be big but it needs to offer what programmers need in one way or another. It's irrelevant whether that's part of the language or part of an ecosystem.
> Languages get popular if they allow programmers to achieve what they want to achieve with minimum effort. It doesn't matter whether they are large or small
For any sufficiently large organisation or lasting use cases a language is almost not at all about writing it but about reading it. Any subsequent modifications after the initial creation will require reading and hopefully understanding what is happening. Even if the code is properly documented the code is always the final specification and if you can't read, you can't understand what your organisation is really doing.
In that sense, adding a feature that is possibly detrimental to reading the language means detrimental to the whole language and its users.
I'm a big fan of Ada, and of all languages I've seen it is by far the most readable. It has been deliberately and carefully designed to be self-documenting. The Ada developers shared your vision. But it's also the reason why I don't think it's true that readability plays a major role in language adoption.
A big reason for C popularity was that for many, it came free with OS, especially in university.
In environment with less "language X is shipped by default", it had much less impact except for crossing over of developers who did work with it on other platforms (which is how Turbo Pascal/Delphi kept on significant marketshare in PC space for a long time).
Yup, gwbasic and qbasic shipped by default on most PC's, and this didn't exactly hurt their popularity. Similarly, one reason JS is so incredibly popular today is that you can start hacking stuff in it with just notepad and a web browser.
It also became popular because in the 1980s it was a way of wringing decent performance out of x86 machines (then becoming popular) without having to write everything in assembler.
It wasn't unique or even super popular on that front, at least not everywhere. In fact, in some places, Borland's Turbo Pascal was the king, with C second.
C domination really was linked more to network effects in my experience.
Naturally they ignore the size of ISO C and its implementation specific nuances, the set of extensions each compiler provides, every system that matters also brings POSIX for the ride and it isn't fully portable with plenty of room for interpretations, the amount of build systems to rely on, the tooling to actually write safe code,...
I've been copying the same hashtable.[ch] file to all my new projects since the 90s. :-) I suppose it could be viewed as a strength of C that it lives below that level of abstraction and prescribe too much of the standard library.
I program multiple languages including Common Lisp.
I don't buy that Common Lisp is too large. I would say the opposite is true. The base language is rather simple and what you mistake for language are just standard library.
What is complex is it being Lisp language which means you need to adjust your brain structure to a different level of functioning and focus when reading code.
**
But languages do get too large.
I think the reason languages get too large is because developers, as soon as they get some experience, start demanding more features. Maybe they want to have more functional experience in an object-oriented language, or maybe they want async in one that was not designed for it.
These developers form a core of users that enjoys being able to absorb these features over time as the language evolves.
On the other hand, new users need to learn it all at once.
There will come a point where new users will just look at horrendously complicated language with vast specification and will just say "Fuck it, I don't want to spend so much time to be able to do anything, I will program in this neat new other language that is simple to start and already has most of what I need."
I lost count of the number of posts in the D language forums along the lines of "don't add any more features, just fix bugs. BTW, I need features X and Y, please implement them."
That made me crack up. Thanks for the morning smile. Priority variation is one thing, but boy oh boy..add lack of self-awareness and you have a pickle of a problem.
I have been programming Java for the past 20 years and I do enjoy new features. And I would like couple more features myself.
But I am also tech lead for a lot of new developers which I see they struggle finding sense in what is going on with Java. Not just the language, but the multitude of frameworks and other paraphernalia supposedly needed to make a working modern application.
Rather than trying to understand it they are giving up and revert to copying and pasting code that works. And wait for somebody with experience to catch obvious problems on code review.
I got new appreciation when it became my responsibility to care for productivity of a lot of people.
Often people bring in new toys just to have some change in their work environment. I select for people who like to learn new things so why would I fault them for it?
But I also see people don't understand how disruptive it is to productivity. People mostly see only gain side without seeing the outsize cost side of the problem.
Even management doesn't seem to understand it. One time they brought me into large project that had trouble delivering and huge reliability problems and they told me my first priority problem is going to figure out how to upgrade Java from 1.6 to 11.
My first question was "Why do you guys think it wasn't possible to deliver reliable applications with Java 1.6?"
> We have an informal rule that every feature that starts with the words "Why not ..." gets rejected.
I wonder how much language inertia can be overcome by surveying what's been implemented multiple times and ranking them.
One imagines a very minimal language with multiple implementations, say, of asynchronous I/O or common data structures. Frequently-implemented features would be good candidates for thoughtful inclusion.
I'm a Common Lisp user, and I would like to refine your claim. I don't think that many true "language features", like borrow checking and a static type system, can be implemented in a Lisp (without writing an entirely new language on top - Lisps are generally Turing complete, so you can always write an interpreter for another language on top, but I would claim that at that point you're not "extending your Lisp" so much as making a new one).
Instead, what Lisps seem to excel at is allowing you to implement syntactic sugar. For instance, you can easily write CL macros that give you JSON/hashtable literal syntax, list comprehensions (pretend LOOP didn't exist), foo.bar to access struct elements, and infix notation - those things that people think of as being "part of the language" but don't give you any new capabilities (e.g. closures, a MOP, multiple-value returns, conditions).
> In a proper Lisp you can take anything that in a regular language would be a language feature and implement it as a library.
I should have been perhaps a bit more clear, though T9 makes that hard. I mean more in the sense of standardization. In some ways, LISP makes a better case for what I've suggested simply because you can modify the syntax with such relative ease. If you see patterns emerge, it makes it easier to standardize a syntactic construction to make it less idiosyncratic.
With a bit of outside perspective, the Java ecosystem “solved” a lot of things with frameworks that are language features elsewhere (e.g., modules). So unfortunately, keeping the language simple can sometimes even increase the overall complexity. I think that’s a tricky thing to balance.
What I find hard in modern Java (well more Scala in my case) is not so much that the language is big but that so many similar features means it's hard to see where things come in scope from.
Like in Scala it could be from a trait (and traits can define variables), or inheritance, or implicit values.
Compared to Rust or Go for example where I can read the docs for a struct, and easily see where the different parts come from (and follow those up if I want).
That's what happens when the language has "history".
Of course when they design a new language they try to incorporate all the features that developers demand at the moment in a way that makes sense.
But once the language is developed it is difficult to add new features seamlessly. The new features are necessarily impaired in some way by having to be compatible with existing environment.
Rest assured, wait couple decades and the same will happen to Rust.
Similar experience, what I learned with Java and I see Go going through exactly the same mistakes, is that when the language is too small, the complexity lands on the eco-system.
That is how you get AOP frameworks, code generators based on EMF, XML driven frameworks,....
The current nightmare of Java programming is Annotation Driven Development.
Especially with things like Spring Boot, most of the behavior is driven by sprinkling annotations, but you can not use your understanding of the syntax and semantics of the Java language to understand what they do. It's just thrashing around trying different combinations of annotations and configuration until something works.
And God help you if you need to debug what's going on if it stops working on some corner case.
This can be stated more generically by saying, that in the past it was possible to tell what a line of code does in Java. This is what you pay for when you bear costs of strong typing -- being able to say what is that thing holding.
But that very important valuable feature was squandered over time with the biggest offender being Spring.
> What is complex is it being Lisp language which means you need to adjust your brain structure to a different level of functioning and focus when reading code.
"To be fair, you have to have a very high IQ to understand Common Lisp..."
I think it has more to do with habits of the mind.
You need to be willing to explore and be inquisitive.
You need to be able to read more difficult books (than entry level) like Let over Lambda or On Lisp and try to understand every sentence and every example and not just the superficial side but also as questions like why this way, when this is useful, what are limits on application, etc.
I guess intelligence correlates but I know a lot of very intelligent people who are too lazy to be bothered to understand things deeply.
It would be fair to say you need at least some IQ to be able to do this. Probably about 100-110 is a limit below which people just are not able to understand complex abstractions (which is a little scary if you think about it, because that is over half of the population). But I wouldn't say you need to be very intelligent (like 140 and upwards).
Nope not at all. Learning Lisp/Common Lisp/Scheme was easy for me. However learning Haskell was hard. Things are hard to learn when you have to change how you think to understand it. I know people who are good at Haskell but crap at writing C. It has nothing to do with IQ.
Python is heading this way (among other problems with the whol shebang). "We had X in this language Y, so add feature X." Why leave a language if it had the features you wanted? And if you left it, why import the problems you tried to leave behind?
> Python is heading this way (among other problems with the whol shebang). "We had X in this language Y, so add feature X." Why leave a language if it had the features you wanted?
Because you can't switch languages in a system for each feature, so if you want features A, B, C, D, and E, and the subset (A,B,C,D) has more value than (D,E), the language that has A,B,C,D will be preferred to the one with D,E, but you’ll still want to add E.
> What is complex is it being Lisp language which means you need to adjust your brain structure to a different level of functioning and focus when reading code.
Even if you do get proficient at this, there is still the problem that reading CL is not enough - it's CL + custom macro/DSL systems per project. Given a bunch of different projects and teams in an org, moving people around is easier with other languages because that second part tends to be less complicated. We have one team that decided to go all in on customizing the common frameworks, inventing their own with bespoke annotations and patterns so now they are basically isolated from the rest of the org and others dread working on projects that involve changes in that codebase.
How is this any different from building an overly convoluted architecture in any other language where you have layers upon layers of objects, getters, setters, proxys, callbacks, VisitorFactoryFactory's, configuration files, multi-level build systems, virtual methods, type system hacks, generic functions?
I've been programming in CL for years now, and I have more of a problem keeping my non-macro code from spiraling out of control than my macros. What makes macros any more than just a thing you have to exercise discipline with, like "normal" code?
> Even if you do get proficient at this, there is still the problem that reading CL is not enough - it's CL + custom macro/DSL systems per project.
That is true and expected.
Because if you "got" CL it means you start any larger application by writing a perfect language in which to write that application.
But with power comes responsibility.
And this is where a lot of Lisp projects fall short.
It is much more difficult to make a mess in a poor language like Java where you can basically ctrl-c/ctrl-v your solution than it is in super powerful language like Lisp that gives you no guidance on how to structure your application. Especially if you also have intelligent people on the project.
Because intelligent people can make even more complicated constructs with which to shoot their feet.
I wish every CL project had one guy with a long beard that can bring wisdom on how to deal with an infinite power of Lisp.
> Once a language gets beyond a certain complexity — say LaTeX, Common Lisp, C++, PL/1, modern Java
Nevertheless Common Lisp, C++, let a lone Java (I know nothing about PL/1) at least are reasonably easy to grok, read and write. At the same time LaTeX seems a language of extraterrestrial insectoids as soon as you go beyond composing formulae and substituting text in pre-made templates and and want really custom typesetting. I once felt like I would like to actually learn it, tried it and gave up completely quickly - it really feels unapproachable.
Before `class`, people built ad-hoc class-like constructs based on prototype inheritance. Often in slightly different ways, with different constructor choices and different implementation details.
For larger code bases in team environments class is a really critical standardization, otherwise you can easily end up with a mess.
It's also important for tooling, because IDEs can much better understand what's going on and present information better.
Now, if Javascript really needs things like private class members and a lot of the other difficulties around classes is another question.
What I found in TeX is that when I was stuck with how to do that? in the 99% of the times somebody had faced and solved the same problem before, not matter how extravagant. Do you want that? sure. Do this. So there is a possible solution for it that does not need to start doing microsurgery with the extra-arcane parts of the language. There was several very good books about Latex also. Is not easy always but definitely is doable.
Lisp is like being over a small table really drunk in the middle of the oceanic storm. The points of reference are moving all the time and the documentation system basically hates you.
> What I found in TeX is that when I was stuck with how to do that? in the 99% of the times somebody had faced and solved the same problem before, not matter how extravagant. Do you want that? sure.
This true and really nice, as long as long as you are Okay with copy-pasting everything mindlessly without serious customizations. It's like being a wizard who would invoke some specific spells by reciting specific phrases in an ancient language without having any idea of the actual language and the nature of the magic while knowing them would let him achieve much much more, easily and elegantly.
Or you can invest some money into buying books and invest lots of time learning a language that is a standard in scientific publication, as I did. Everything can be learnt. Is just a question of will, money and time.
In fact I'm not interested in it being a standard in scientific publication nor in investing a huge amount of time. I just want a typesetting language which would be more practical and more powerful than HTML+CSS yet reasonably easy to learn and use in creative and practical ways. Unfortunately I don't know of any typesetting languages other than TeX and HTML+CSS.
While I do agree that title of the article is clickbaity. I also agree with most of the arguments in the article itself.
I'm a big fan of JavaScript as being a simple functional language with tiny bit of prototype based OOP. I do like some of the newer features of the language - arrow functions, block scoped variables, destructuring...
But I'm not sure stuff we're adding now are really giving us any value: the ugly async/await that is leaking abstractions and needs a lot of fixes to support iteration, classes, static members, constructors, annotations, etc.
For me personally it adds nothing valuable to the language. It simply is there to make language attractive to C# and Java programmers. It clashes with current libraries and way we used to work with JS. Thanks to that, we'll probably see explosion of crazy complex tools from these languages being introduced into JS - ORMs, DI containers, code generators and dynamic proxies everywhere...
Most of our problems could be easily solved using functions, objects and closures. But now we'll have Spring Data JS and in few years we'll also get our SimpleBeanFactoryAwareAspectInstanceFactory.
As soon as you see a Builder a Container is sure to follow then you need a Factory for your Containers and a Proxy to mediate access to your Factory. Alternatively you can opt for Clojurescript and never have to deal with any of this crap.
The problem with growing languages is often age more than size alone. Some features turn out to be less useful, or will later be superseded by more general features. But beginners still have to learn all of the the old ways to understand existing code.
C++ is perhaps the most prominent example of this, I’d consider it to be at least three different languages merged into one at this point. JavaScript has also accumulated some weird stuff over the years, like var, == and (arguably) a class-based OO system that doesn’t interact very well with the rest of the language.
One “solution” is using only young languages (e.g. Rust is very large, but young enough that most of its features are still useful). I personally like switching technologies every few years, but as a field, we can’t keep doing that forever. The other idea is using a language that’s more or less frozen, like C.
I like the way that Scheme dealt with this recent-ish-ly, even if it hasn't gained overwhelming support in the community.
R6RS was considered too big, and too influenced by one of the players, by a large amount of the community.
So, the big player distanced themselves a bit, renaming themselves to Racket, etc.
But for the next standard, two were produced. R7RS-small and R7RS-large. A core language that stays the same, that everyone has to use to call themselves a "Revision 7 Scheme", and an extended standard language that is useful, for those wanting to provide a "batteries included" kind of environment.
Relatively speaking, those changes are mild. And it’s the first language extension in a decade (since C11). Compared to the insanity that the C++ committee does every three years, that’s a good thing.
I see this happening with Go, a language with a specific design goal of remaining simple.
Generics getting added is OK. I mean, almost every veteran Gopher I've talked to agrees that there's been a couple of times, maybe, that they've needed generics and not had them, and that interfaces work fine for 99% of cases. Everyone outside the language insists that it needs generics. But sure, let's add generics for those few times we need it [0].
But then "error handling is too onerous", "there's too much boiler plate", "why can't I code in Go using a functional style?", "Go needs to look more like Ruby", etc, etc.
I really hope they keep Go simple and don't cave in to demands to add stuff to it. It doesn't need more syntax. It needs smaller runtimes, a method of determining whether two slices are using the same backing array, and a few other gotchas.
I can see a future where there's a "Go classic" forked from 1.17 (or possibly 1.18) that never moves forward, while the main Go gets ever more complex and Java-fied.
[0] Except, of course, that as soon as we have generics then every new gopher will insist on using them for everything, and not learn how to use interfaces well instead.
> I can see a future where there's a "Go classic" forked from 1.17 (or possibly 1.18) that never moves forward, while the main Go gets ever more complex and Java-fied.
What I think will actually happen is that Go will continue increasing in size, generics will bring a new kind of fragmentation with people using libraries everywhere to replace the base constructs of the language, and in 10 years a new "minimalist" language to replace Go will appear, and the cycle will continue.
You see that happening with pretty much everything. At first the new thing is easy to learn, and then it slowly becomes as large and hard to learn as the thing it replaced, because accidental complexity wasn't such a big part of the original thing.
This article barely mentions Common Lisp. The author makes a good point that for complicated languages, programmers carve out a subset of features to get their work done. (At least, this is what I do for Common Lisp, Haskell, Swift, etc.)
I have always loved a tech world with many programming languages, but I wonder if this might change in a few decades if software reliability becomes more of an issue. I don’t know much Rust, but I have taken a good look at Swift as being a possible direction for a universal language (REPL development, strongly typed with fast compilation speeds on M1, and a generally pleasant language).
I have a prediction: IT security will become such a larger problem in the future that programming languages and operating systems will go through a winnowing out phase, fewer choices, and those fewer choices will have more effort in producing provably correct software.
Scott Meyers, in Effective C++, writes at the outset that C++ is a federation of languages and that one must come to an agreement at the beginning of a project about which parts of the federated language will be used. I think Java is in the same ball-park these days.
Common Lisp has little syntax (but complicated by macros), 900+ functions in the language spec, supports many programming paradigms, and yet I don't feel it as having become fragmented as a language.
Your winnowing idea is interesting, but would seem to suggest we periodically discard entire languages. A language's library ecosystem is often a critical factor in selection, which also makes it harder to give up.
The longer a language is actively developed, it seems there's a greater risk for the language to become fragmented. I wonder if this is ever a concern for language designers (I presume it is) and what sorts of decision might lessen the probability of fragmentation occurring in the future.
> I have always loved a tech world with many programming languages, but I wonder if this might change in a few decades if software reliability becomes more of an issue.
I don't expect that to happen unless the liability landscape or some other financial incentive changes. Do you see force that would drive an increased focus on reliability in the industry?
Lots of big languages are successful; C++ is a prime example.
My understanding of Lisp 'history' is that most CL implementations were by large vendors charging silly licensing fees and they got undercut by C and Pascal vendors...
Another theory I have is that Lisp encourages the use of macros which makes sharing code more difficult.
Or maybe it's simply that the languages that built/were built on Unix and PCs became most popular as those systems were most popular (C, C++, Perl, Python, Ruby, etc...). And of course now the language of the browser (JS).
Or maybe a bit of everything. On the plus side, SBCL is pretty fantastic and people still do things with Lisp.
Common Lisp is an extremely compact, regular and small language. It comes with a huge standard library that you can but don’t have to use. By that measure, Javascript would be a large language because it has a big NPM ecosystem. Common Lisp wanted to avoid the problem so that people don’t have to write seven versions of the left pad library. As a result it includes useful stuff in the standard library.
This is probably a bit unfair, because technically things like the object system and conditions and loop are part of the standard library, but they can also be considered part of the core language given their importance in much CL software.
I know it is. That's not the point. The point is that you need to know CLOS with its many rules to understand many CL code bases, just like you need to understand C++ classes.
The fact that C++ classes are part of the C++ core langauge while CLOS is a library doesn't really change this aspect.
I agree with the claim that knowing CLOS is useful to read code bases. Where I think we differ is that I don’t consider CLOS a part of the language itself. I consider it a part of a particular implementation of the language. You can be a fluent and prolific CL programmer without ever writing any code that relies on CLOS, defstruct or defclass, etc.
The big difference between C++ and CL in this particular instance is that you can’t write C++ without writing or using classes/structs. For example, using STL almost always involves using or writing custom comparator “classes”. Even if you choose to go with lambdas, the whole concept of a container is object oriented.
In CL, you can, if you want, stick with lists all the way down without ever touching defclass/defstruct at all. An equivalent abstraction level of the C++ code would only use integral data types. Whereas in CL those types are part of the language.
Comparing CL to C++ isn't very fair. That's like comparing a quartz-watch with multiple displays and a set of carefully laid-out knobs to a mad hairball of debris which will sting you every time you try to pick it up / comb it / whatever. And OK, maybe their formal descriptions both will run similar length. But it's not a fair comparison.
Size isn't all, what it really is is comprehensibility. Perception of its essence. If the foundations are solid, if you don't have a feeling that it might just be an ad hoc contraption mucked up by the devil himself to make people suffer, if it feels like you don't have to fight it every step of the way, if you don't get nauseous just by thinking about it, you just might as well stroll through it, no matter the pathwalk length.
Maybe a better question is, what's really extraneous? You know, with CL, I can't immediately tell. The Scheme folks have an idea. But Scheme and CL still look like a pair of siblings nonetheless.
Or maybe an even more fundamental question is: "is it well-designed"? Or maybe "does it provide depth without imposing unnecessary complexity"?
There are plenty of questions, but the size one is quite boring, honestly.
Yep I agree that it isn’t fair to compare the two languages. C/C++ is the language that runs the world. Including the OS you are using right now. CL is a niche language that close to nobody use in production. It’s like comparing a container ship with a paper drawing of a ship. No comparison.
It occurs to me that there are very different penalties for growing the standard library vs the language itself.
Adding new things to the language (e.g. closures, conditions, a borrow checker, first-class functions, immutable variables, multiple return values) results in the user having to keep more things in their head at once to use the language.
Adding new things to the standard library increases the size of the language distribution, but doesn't appear to increase cognitive load that much. The fact that CL has a `round` function doesn't really make programming in it any harder.
Adding syntactic sugar to the language can make it harder to read, and usually breaks backward-compatibility.
My impression is that most language "bloat" is in the standard library and syntactic sugar, not the language "core" - although I have little experience with C++. Any counter-impressions?
I think increasing the size of the standard libraries is the solution to avoiding core language bloat. C++ has mostly taken this approach.
A bloated core language means that programmers now have a lot of baggage to deal with--particularly features that reduce performance. In my opinion, I do not think features that reduce performance should ever be added to a core language. The only features that should be added to a core language are ones that increase code readability and safety and reduce the amount of code you need to write to accomplish your goal.
> Bjarne was resolute in preserving his fundamental goals … People were telling Bjarne that you should turn C++ into a fully object oriented language… make it automatically garbage collectable… make all the member functions virtual… eliminate global functions… in other words they wanted him to design Java. He listened politely to them and kept maintaining the fundamental integrity of the language. [0]
I kinda like large languages though. C# is very much a "sharks with frikking laser beams" kitchen sink of paradigms and features (fun fact, it even has dynamic typing!) but it's a joy to program in, because I have options and a lot of ways to express myself. the dark side of having minimal syntax is that code accumulates boilerplate, design patterns and frameworks.
I agree with the author's point that JavaScript is getting too big too fast. I don't like all the new support for OO stuff. I don't like how TypeScript often looks like C#. I don't like the direction in which JavaScript is heading. I may very biased here, but I think things like the promise proposal not being a monad is an example of those small cuts that kill slowly. Instead of having a well-thought abstraction, we have a "quick fix" around some problems, but then we have a quick fix on the quick fix (async/await). I wonder what the future of JS holds, but I'm not too confident.
116 comments
[ 2.7 ms ] story [ 204 ms ] thread2019: https://news.ycombinator.com/item?id=20233220
2015: https://news.ycombinator.com/item?id=9738866
CL honestly isn't a large language by modern standards. The Common Lisp standard library is tiny compared to modern C++, Java, Python, Ruby, Rust, Haskell, APL, or whatever other mainstream language one is working with nowadays. CL might have been "batteries-included" in 1994, when it was standardized, but the idea of "batteries" that a language can and "should" include has been expanded hundreds of times since then.
I understand why people might get nostalgic for simpler times and simpler computers, I'm partial to the rose coloured glasses view of the past myself.
Its been interesting using C# for the last 15 years through that perspective. Microsoft seem intent on folding every cool language feature from every programming language into .NET and it does feel overwhelming, but in the end most experienced programmers have a style so we naturally settle on a subset of language features until we need something new. I really wish somebody had implemented the async patterns in mainstream languages in the 1990s for example.
I really don't think nostalgia for simpler times is useful for solving todays problems, I much prefer C# to Smalltalk or lisp for getting stuff done today.
The language itself has adopted just about everything interesting from contemporary languages, from pattern matching to top level statements allowing you to write a hello world or simple scripts that rival python for simplicity.
It’s performant, asp.net is at the top of the tech empower benchmarks, c# is only just behind c++ on the compiler benchamrks game leaderboards (although if you actually read the code i question the value of the deceptive impls there, e.g. the regex redux example is just calling a c library rather than using native c# facilities).
It’s not as memory efficient as go, by about 5x-10x worse but it’s comparable to java. The clr has a much faster allocator than go (although go developers just pre-allocate up front as an idiomatic optimisation).
Since dotnet 5.0 the developer experience is very decent across all platforms. 6.0 with its further enhanced hot reload etc looks even better.
It’s cross platform - apple tv apps or apple watch, ios, android, windows, linux, macos, they’re all first class citizens.
Whether you want to write 3d games (ubiquity), machine learning, web apps, desktop apps, mobile apps, wasm / blazor, it has 1st class support for them all.
And yet, i can’t help but feel common lisp is probably a better language.
The developer experience in CL is superior - repl driven development combined with structural editing and easily accessible ast manipulation just put CL (and other lisps) beyond what more static languages can ever hope to offer in terms of developer ergonomics.
Both could be added to common lisp though - the vector arg list would be a short reader-macro and my lisp-foo isn't yet strong enough to say how to implement the keyword as a function thing but i'm 100% confident it can be done...)
You'll find 2 C# regex redux programs when you look at all the regex redux programs —
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
— or look at all the C# programs —
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
One uses "native c# facilities" —
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
— and the other uses PCRE2 —
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
There's nothing "deceptive".
You are explicitly told to look at the other programs.
The language is described in about 40 pages of said standard.
Common Lisp 1994 (2004 edition): 1153 pages
C 1999: 554 pages
C++ 1998: 776 pages
C++ 2011: 1356 pages
C++ 2014: 1375 pages
C++ 2017: 1618 pages
C++ 2017(2018): 1620 pages
In comparison, the reference manual for Ada 1983 had only 336 pages and the Algol 68 report had only 146 pages, these 2 being considered very large languages at their time.
It is true, however, that the Algol 68 report would have required a double length to include enough explanations to make it intelligible for everybody.
I have a copy of MIL-STD-1815, the first Ada spec, and it's 242 pages!
At that stage, Ada had already won the DoD competition, but until it was published as a military standard a few more features were added and some sections of the specification were rewritten, for increased precision.
In any case, the June 1979 Ada version already included what I consider as the most useful Ada innovation, even if it is just a small detail: allowing underscores inside numbers, for better readability.
It always annoys me when I have to use some programming language or software tool that happens to miss this feature, even now, more than 40 years after it became known that this is a good idea.
The rationale for this surprising choice was that a number starting with an underscore could be interpreted as an identifier.
??!! Why would anyone want to start a number with an underscore? Underscores are needed only inside long numbers.
The syntax should have allowed underscores only inside numbers and there would have been no problems with backward compatibility, as such numbers would have been illegal before.
The single quote cannot have been easier for parsing as the new use must not be confused by the parser with character literals, which can appear in the same context.
The underscore was first introduced in a programming language by IBM PL/I (in 1964, when it was still named NPL) with the precise purpose of improving the readability of identifiers.
Ada has extended this use to also improve the readability of long numbers.
Why C++ has chosen a different character, which is normally used for completely different purposes, without having any decent justification for such a baffling decision, I cannot understand.
https://dl.acm.org/doi/abs/10.1145/3386323
Since D popularized it, many other languages added it. Even C is finally doing it (although using a ' instead of _, which I do not understand the rationale for).
Had you added Java, .NET or Python, with their standard libraries, C++ would look like a toy language.
POSIX 2017 has 3951 pages !
Adding those to the current C standard would make about 4700 pages, a little more than 4 times the size of the Common Lisp standard.
Here you go, three reasons why CL is less popular than it could be that have nothing to do with the size of the language, and these are just examples. A language doesn't need to be big but it needs to offer what programmers need in one way or another. It's irrelevant whether that's part of the language or part of an ecosystem.
For any sufficiently large organisation or lasting use cases a language is almost not at all about writing it but about reading it. Any subsequent modifications after the initial creation will require reading and hopefully understanding what is happening. Even if the code is properly documented the code is always the final specification and if you can't read, you can't understand what your organisation is really doing.
In that sense, adding a feature that is possibly detrimental to reading the language means detrimental to the whole language and its users.
This is very true, and is one of the reasons our workplace moved to Python. Readability is very, very important.
In environment with less "language X is shipped by default", it had much less impact except for crossing over of developers who did work with it on other platforms (which is how Turbo Pascal/Delphi kept on significant marketshare in PC space for a long time).
C domination really was linked more to network effects in my experience.
Naturally they ignore the size of ISO C and its implementation specific nuances, the set of extensions each compiler provides, every system that matters also brings POSIX for the ride and it isn't fully portable with plenty of room for interpretations, the amount of build systems to rely on, the tooling to actually write safe code,...
But hey, the K&R C book is small.
EDIT: or just use redis lol
I don't buy that Common Lisp is too large. I would say the opposite is true. The base language is rather simple and what you mistake for language are just standard library.
What is complex is it being Lisp language which means you need to adjust your brain structure to a different level of functioning and focus when reading code.
**
But languages do get too large.
I think the reason languages get too large is because developers, as soon as they get some experience, start demanding more features. Maybe they want to have more functional experience in an object-oriented language, or maybe they want async in one that was not designed for it.
These developers form a core of users that enjoys being able to absorb these features over time as the language evolves.
On the other hand, new users need to learn it all at once.
There will come a point where new users will just look at horrendously complicated language with vast specification and will just say "Fuck it, I don't want to spend so much time to be able to do anything, I will program in this neat new other language that is simple to start and already has most of what I need."
I have been programming Java for the past 20 years and I do enjoy new features. And I would like couple more features myself.
But I am also tech lead for a lot of new developers which I see they struggle finding sense in what is going on with Java. Not just the language, but the multitude of frameworks and other paraphernalia supposedly needed to make a working modern application.
Rather than trying to understand it they are giving up and revert to copying and pasting code that works. And wait for somebody with experience to catch obvious problems on code review.
I think this is not sustainable.
Often, though, I just feel like Doctor No.
I got new appreciation when it became my responsibility to care for productivity of a lot of people.
Often people bring in new toys just to have some change in their work environment. I select for people who like to learn new things so why would I fault them for it?
But I also see people don't understand how disruptive it is to productivity. People mostly see only gain side without seeing the outsize cost side of the problem.
Even management doesn't seem to understand it. One time they brought me into large project that had trouble delivering and huge reliability problems and they told me my first priority problem is going to figure out how to upgrade Java from 1.6 to 11.
My first question was "Why do you guys think it wasn't possible to deliver reliable applications with Java 1.6?"
I wonder how much language inertia can be overcome by surveying what's been implemented multiple times and ranking them.
One imagines a very minimal language with multiple implementations, say, of asynchronous I/O or common data structures. Frequently-implemented features would be good candidates for thoughtful inclusion.
In a proper Lisp you can take anything that in a regular language would be a language feature and implement it as a library.
Instead, what Lisps seem to excel at is allowing you to implement syntactic sugar. For instance, you can easily write CL macros that give you JSON/hashtable literal syntax, list comprehensions (pretend LOOP didn't exist), foo.bar to access struct elements, and infix notation - those things that people think of as being "part of the language" but don't give you any new capabilities (e.g. closures, a MOP, multiple-value returns, conditions).
I should have been perhaps a bit more clear, though T9 makes that hard. I mean more in the sense of standardization. In some ways, LISP makes a better case for what I've suggested simply because you can modify the syntax with such relative ease. If you see patterns emerge, it makes it easier to standardize a syntactic construction to make it less idiosyncratic.
Like in Scala it could be from a trait (and traits can define variables), or inheritance, or implicit values.
Compared to Rust or Go for example where I can read the docs for a struct, and easily see where the different parts come from (and follow those up if I want).
Of course when they design a new language they try to incorporate all the features that developers demand at the moment in a way that makes sense.
But once the language is developed it is difficult to add new features seamlessly. The new features are necessarily impaired in some way by having to be compatible with existing environment.
Rest assured, wait couple decades and the same will happen to Rust.
That is how you get AOP frameworks, code generators based on EMF, XML driven frameworks,....
Especially with things like Spring Boot, most of the behavior is driven by sprinkling annotations, but you can not use your understanding of the syntax and semantics of the Java language to understand what they do. It's just thrashing around trying different combinations of annotations and configuration until something works.
And God help you if you need to debug what's going on if it stops working on some corner case.
But that very important valuable feature was squandered over time with the biggest offender being Spring.
"To be fair, you have to have a very high IQ to understand Common Lisp..."
You need to be willing to explore and be inquisitive.
You need to be able to read more difficult books (than entry level) like Let over Lambda or On Lisp and try to understand every sentence and every example and not just the superficial side but also as questions like why this way, when this is useful, what are limits on application, etc.
I guess intelligence correlates but I know a lot of very intelligent people who are too lazy to be bothered to understand things deeply.
It would be fair to say you need at least some IQ to be able to do this. Probably about 100-110 is a limit below which people just are not able to understand complex abstractions (which is a little scary if you think about it, because that is over half of the population). But I wouldn't say you need to be very intelligent (like 140 and upwards).
Because you can't switch languages in a system for each feature, so if you want features A, B, C, D, and E, and the subset (A,B,C,D) has more value than (D,E), the language that has A,B,C,D will be preferred to the one with D,E, but you’ll still want to add E.
Even if you do get proficient at this, there is still the problem that reading CL is not enough - it's CL + custom macro/DSL systems per project. Given a bunch of different projects and teams in an org, moving people around is easier with other languages because that second part tends to be less complicated. We have one team that decided to go all in on customizing the common frameworks, inventing their own with bespoke annotations and patterns so now they are basically isolated from the rest of the org and others dread working on projects that involve changes in that codebase.
How is this any different from building an overly convoluted architecture in any other language where you have layers upon layers of objects, getters, setters, proxys, callbacks, VisitorFactoryFactory's, configuration files, multi-level build systems, virtual methods, type system hacks, generic functions?
I've been programming in CL for years now, and I have more of a problem keeping my non-macro code from spiraling out of control than my macros. What makes macros any more than just a thing you have to exercise discipline with, like "normal" code?
That is true and expected.
Because if you "got" CL it means you start any larger application by writing a perfect language in which to write that application.
But with power comes responsibility.
And this is where a lot of Lisp projects fall short.
It is much more difficult to make a mess in a poor language like Java where you can basically ctrl-c/ctrl-v your solution than it is in super powerful language like Lisp that gives you no guidance on how to structure your application. Especially if you also have intelligent people on the project.
Because intelligent people can make even more complicated constructs with which to shoot their feet.
I wish every CL project had one guy with a long beard that can bring wisdom on how to deal with an infinite power of Lisp.
Nevertheless Common Lisp, C++, let a lone Java (I know nothing about PL/1) at least are reasonably easy to grok, read and write. At the same time LaTeX seems a language of extraterrestrial insectoids as soon as you go beyond composing formulae and substituting text in pre-made templates and and want really custom typesetting. I once felt like I would like to actually learn it, tried it and gave up completely quickly - it really feels unapproachable.
Before `class`, people built ad-hoc class-like constructs based on prototype inheritance. Often in slightly different ways, with different constructor choices and different implementation details.
For larger code bases in team environments class is a really critical standardization, otherwise you can easily end up with a mess.
It's also important for tooling, because IDEs can much better understand what's going on and present information better.
Now, if Javascript really needs things like private class members and a lot of the other difficulties around classes is another question.
Lisp is like being over a small table really drunk in the middle of the oceanic storm. The points of reference are moving all the time and the documentation system basically hates you.
apt install slime sbcl
Always stubborn like a horse
This true and really nice, as long as long as you are Okay with copy-pasting everything mindlessly without serious customizations. It's like being a wizard who would invoke some specific spells by reciting specific phrases in an ancient language without having any idea of the actual language and the nature of the magic while knowing them would let him achieve much much more, easily and elegantly.
I'm a big fan of JavaScript as being a simple functional language with tiny bit of prototype based OOP. I do like some of the newer features of the language - arrow functions, block scoped variables, destructuring...
But I'm not sure stuff we're adding now are really giving us any value: the ugly async/await that is leaking abstractions and needs a lot of fixes to support iteration, classes, static members, constructors, annotations, etc.
For me personally it adds nothing valuable to the language. It simply is there to make language attractive to C# and Java programmers. It clashes with current libraries and way we used to work with JS. Thanks to that, we'll probably see explosion of crazy complex tools from these languages being introduced into JS - ORMs, DI containers, code generators and dynamic proxies everywhere...
Most of our problems could be easily solved using functions, objects and closures. But now we'll have Spring Data JS and in few years we'll also get our SimpleBeanFactoryAwareAspectInstanceFactory.
C++ is perhaps the most prominent example of this, I’d consider it to be at least three different languages merged into one at this point. JavaScript has also accumulated some weird stuff over the years, like var, == and (arguably) a class-based OO system that doesn’t interact very well with the rest of the language.
One “solution” is using only young languages (e.g. Rust is very large, but young enough that most of its features are still useful). I personally like switching technologies every few years, but as a field, we can’t keep doing that forever. The other idea is using a language that’s more or less frozen, like C.
R6RS was considered too big, and too influenced by one of the players, by a large amount of the community.
So, the big player distanced themselves a bit, renaming themselves to Racket, etc.
But for the next standard, two were produced. R7RS-small and R7RS-large. A core language that stays the same, that everyone has to use to call themselves a "Revision 7 Scheme", and an extended standard language that is useful, for those wanting to provide a "batteries included" kind of environment.
And no one is obliged to use every single feature from C++.
Generics getting added is OK. I mean, almost every veteran Gopher I've talked to agrees that there's been a couple of times, maybe, that they've needed generics and not had them, and that interfaces work fine for 99% of cases. Everyone outside the language insists that it needs generics. But sure, let's add generics for those few times we need it [0].
But then "error handling is too onerous", "there's too much boiler plate", "why can't I code in Go using a functional style?", "Go needs to look more like Ruby", etc, etc.
I really hope they keep Go simple and don't cave in to demands to add stuff to it. It doesn't need more syntax. It needs smaller runtimes, a method of determining whether two slices are using the same backing array, and a few other gotchas.
I can see a future where there's a "Go classic" forked from 1.17 (or possibly 1.18) that never moves forward, while the main Go gets ever more complex and Java-fied.
[0] Except, of course, that as soon as we have generics then every new gopher will insist on using them for everything, and not learn how to use interfaces well instead.
What I think will actually happen is that Go will continue increasing in size, generics will bring a new kind of fragmentation with people using libraries everywhere to replace the base constructs of the language, and in 10 years a new "minimalist" language to replace Go will appear, and the cycle will continue.
You see that happening with pretty much everything. At first the new thing is easy to learn, and then it slowly becomes as large and hard to learn as the thing it replaced, because accidental complexity wasn't such a big part of the original thing.
I have always loved a tech world with many programming languages, but I wonder if this might change in a few decades if software reliability becomes more of an issue. I don’t know much Rust, but I have taken a good look at Swift as being a possible direction for a universal language (REPL development, strongly typed with fast compilation speeds on M1, and a generally pleasant language).
I have a prediction: IT security will become such a larger problem in the future that programming languages and operating systems will go through a winnowing out phase, fewer choices, and those fewer choices will have more effort in producing provably correct software.
Common Lisp has little syntax (but complicated by macros), 900+ functions in the language spec, supports many programming paradigms, and yet I don't feel it as having become fragmented as a language.
Your winnowing idea is interesting, but would seem to suggest we periodically discard entire languages. A language's library ecosystem is often a critical factor in selection, which also makes it harder to give up.
The longer a language is actively developed, it seems there's a greater risk for the language to become fragmented. I wonder if this is ever a concern for language designers (I presume it is) and what sorts of decision might lessen the probability of fragmentation occurring in the future.
> I have always loved a tech world with many programming languages, but I wonder if this might change in a few decades if software reliability becomes more of an issue.
I don't expect that to happen unless the liability landscape or some other financial incentive changes. Do you see force that would drive an increased focus on reliability in the industry?
My understanding of Lisp 'history' is that most CL implementations were by large vendors charging silly licensing fees and they got undercut by C and Pascal vendors...
Another theory I have is that Lisp encourages the use of macros which makes sharing code more difficult.
Or maybe it's simply that the languages that built/were built on Unix and PCs became most popular as those systems were most popular (C, C++, Perl, Python, Ruby, etc...). And of course now the language of the browser (JS).
Or maybe a bit of everything. On the plus side, SBCL is pretty fantastic and people still do things with Lisp.
These two books explain how it’s done:
https://en.m.wikipedia.org/wiki/Object-Oriented_Programming_...
https://en.m.wikipedia.org/wiki/The_Art_of_the_Metaobject_Pr...
The fact that C++ classes are part of the C++ core langauge while CLOS is a library doesn't really change this aspect.
The big difference between C++ and CL in this particular instance is that you can’t write C++ without writing or using classes/structs. For example, using STL almost always involves using or writing custom comparator “classes”. Even if you choose to go with lambdas, the whole concept of a container is object oriented.
In CL, you can, if you want, stick with lists all the way down without ever touching defclass/defstruct at all. An equivalent abstraction level of the C++ code would only use integral data types. Whereas in CL those types are part of the language.
Size isn't all, what it really is is comprehensibility. Perception of its essence. If the foundations are solid, if you don't have a feeling that it might just be an ad hoc contraption mucked up by the devil himself to make people suffer, if it feels like you don't have to fight it every step of the way, if you don't get nauseous just by thinking about it, you just might as well stroll through it, no matter the pathwalk length.
Maybe a better question is, what's really extraneous? You know, with CL, I can't immediately tell. The Scheme folks have an idea. But Scheme and CL still look like a pair of siblings nonetheless.
Or maybe an even more fundamental question is: "is it well-designed"? Or maybe "does it provide depth without imposing unnecessary complexity"?
There are plenty of questions, but the size one is quite boring, honestly.
Adding new things to the language (e.g. closures, conditions, a borrow checker, first-class functions, immutable variables, multiple return values) results in the user having to keep more things in their head at once to use the language.
Adding new things to the standard library increases the size of the language distribution, but doesn't appear to increase cognitive load that much. The fact that CL has a `round` function doesn't really make programming in it any harder.
Adding syntactic sugar to the language can make it harder to read, and usually breaks backward-compatibility.
My impression is that most language "bloat" is in the standard library and syntactic sugar, not the language "core" - although I have little experience with C++. Any counter-impressions?
A bloated core language means that programmers now have a lot of baggage to deal with--particularly features that reduce performance. In my opinion, I do not think features that reduce performance should ever be added to a core language. The only features that should be added to a core language are ones that increase code readability and safety and reduce the amount of code you need to write to accomplish your goal.
> Bjarne was resolute in preserving his fundamental goals … People were telling Bjarne that you should turn C++ into a fully object oriented language… make it automatically garbage collectable… make all the member functions virtual… eliminate global functions… in other words they wanted him to design Java. He listened politely to them and kept maintaining the fundamental integrity of the language. [0]
[0] CppCon 2014: Bjarne Stroustrup "Make Simple Tasks Simple!" (https://youtu.be/nesCaocNjtQ?t=296)