When we look at a new language, what is most likely to impress us is what it has. It’s tempting to distinguish a language by adding built-ins — control structures, thread interaction facilities, resource management apparatus, error handling. These are easy to describe, easy to remember, easy to imagine using.

It’s a trap.

It’s not that there’s anything wrong with any given built-in, per se. They work fine, and help users code. It takes a lot of them to add much complexity to the implementation. C is always described as having a “rich set of operators”, and this is usually counted in its favor. Scripting languages have many more built-in features than C, shell languages even more.

That’s a clue. The less powerful a language is, the more built-ins it needs to attract users. Without built-in process control, pattern matching, and formatted i/o, a shell would be crippled. Users would switch to something else.

Languages we count as powerful get along without any such features. Those features can all be coded directly in the language, and put in a library. The only features we need to build into the core language are those we can’t code in the core language, or that we couldn’t present pleasingly to users if we did. In C, I/O is in the standard library, but for / break / continue is built in. strlen() is in the library, but argument passing is built in.

Let us consider Lisp. Lisp has memory management built in. This is always counted in its favor: Lisp has garbage collection, where (e.g.) C++ lacks garbage collection. This makes Lisp (like Haskell, Python, Java) “higher level” than C++. Could Lisp memory management be coded in Lisp, and put in the library? No way.

For contrast, consider control structures. Lisp needs no built-in for loop. With Lisp macros and lambda you can code your own control structures, and stick them in a library. People do. Common Lisp’s standard library has the most useful ones already. They look built in.

Lisp manages memory automatically, usually well enough for the programs people write in Lisp. What if you have other resources to manage? The programming world provides us plenty of others: sockets, database connections, locks, windows, closures, sessions, contexts. They’re distinguished from memory in that something has to happen when they go away, that the language doesn’t necessarily know about. You can make the language know a few, but it’s always easy to come up with more it doesn’t, or where you need closer control of them than the language offers. Indeed, memory may be one of the latter. My web browser used to pause frequently, each time ignoring UI events for 15 seconds while it garbage-collected Javascript memory.  Nowadays it segfaults when I’m not using it.