In this post I want to make this kind of simplicity more precise and talk about some reasons it’s important. I propose five key ideas for simple programming languages: ready-at-hand features, fast iteration cycles, a single way of doing things, first-order reasoning principles, and simple static type systems. I discuss each of these at length below.

Aside: simplicity in languages is interesting. I’d say most popular languages, from Rust and Haskell to Python and JavaScript, are not simple. Popular PL research topics, such as linear types and effect systems, are also not simple (I suppose all the simple concepts have already been done over and over).

Making a simple language which is also practical requires a careful selection of features: powerful enough to cover all of the language’s possible use-cases, but not too powerful that they encourage over-engineered or unnecessarily-clever (hard-to-understand) solutions (e.g. metaprogramming). The simplest languages tend to be DSLs with very specific use-cases, and the least simple ones tend to have so much complexity, people write simpler DSLs in them. But then, many simple DSLs become complex in aggregate, to implement and to learn…so once again, it’s a balance of “which features have the broadest use-cases while remaining easy to reason about”?

  • Hector_McG@programming.dev
    link
    fedilink
    arrow-up
    4
    ·
    3 months ago

    The machine itself can generally only do very simple things

    That really hasn’t been true for at least 2 decades. And nowadays assembly code is no more that another abstraction layer, as microcode in the processor becomes increasingly complex. It’s as out-of-date an idea as the idea that C code is ‘close to the metal’.

    • BatmanAoD@programming.dev
      link
      fedilink
      arrow-up
      1
      ·
      edit-2
      3 months ago

      I should have said “relatively simple”, not “very simple”. Yes, modern assembly instructions can often be relatively complex (though not on all architectures). But the point is that every abstraction layer presents a simpler API compared to what’s below, but must be implemented in terms of complex combinations of the fundamentally simple units of functionality in the layer below it. This is true of assembly, yes, but that doesn’t make it less true of higher level languages.