People always ask, what's your favorite programming language? But it makes no sense. That's like asking what screwdriver I like best. Um, the one that fits these screws here?
Okay, so sometimes it's more like going to look at art supplies. There's so much to choose from! Colored pencils. Pastels. Markers and gel pens. How am I supposed to pick just one? They all have unique qualities.
Besides, the journey matters as much as the destination.
Programming language popularity matters, but it's not easy (or the best idea) to pick one based on that. You can try choosing based on [[roles|intended role]], but those are often muddled, too.
- By far the most popular language in 2025 is [[Python]].
- [[C++]] is in second place about half the time (when it's not C).
- Another popular scripting language is [[Lua]].
- [[Vala]] is a less popular choice, but apps made with it exist.
- There's even [[Zig]] code out there, even though it's nowhere near ready for prime time, as its own creators warn.
- Despite boasting many sponsors, [[D]] doesn't seem to be used much "in the wild".
- Conversely, [[Nim]] has noticeably grown in recent years.
- Classics like [[Ada]] and [[Fortran]] are alive and well, with healthy communities and numerous real-world uses.
- So are [[Lisp]] and [[Scheme]], or at least certain dialects thereof. But some of those will surprise you!
- Even [[Prolog]] is much more alive than generally believed, not to mention [[Perl]] (despite everyone's protestations).
- Only [[Pascal]] doesn't look so good, but people still use it. Likewise for [[Awk]].
The best language doesn't always win, but it doesn't matter how good your language is when your entire community gives off bad vibes. Software development is a social skill, of which programming is only a small part.
Python
As of 2025, [Python](https://www.python.org/) has been the world's most popular programming language by far, and it gives no signs of slowing down; on the contrary. That means there's a huge ecosystem and readily available support. But it's also a failure of imagination, not to mention a monoculture in the making.
## The good
- It has a very clean syntax, which makes it seem friendly
- Because of that it's often recommended to beginners, but see below
- Has powerful features like generators and list comprehensions
- Comes bundled with Tkinter, still one of the best GUI toolkits out there
- Added gradual typing in recent versions
- Can be compiled to stand-alone binaries with [Nuitka](https://nuitka.net/)
## The bad
- It's a big language, with lots of different concepts to learn upfront
- Needs third-party tools like [MyPy](https://www.mypy-lang.org/) for static typechecking
- The runtime is large, a considerable overhead for stand-alone binaries
## The ugly
- Whitespace-based code structure is inherently fragile, tabs or no tabs
- Classes are the only way to declare new [[types]]
- Enum classes exist in recent versions, but they feel tacked on
While there are multiple Python implementations, CPython utterly dominates; most of the others are specialized anyway. Due to the size and complexity of newer versions, it's hard to start over.
C++
Since C++11, the language has become a powerhouse, offering an unequaled blend of high-level features and low-level performance. This is also its undoing.
* It takes years to become even moderately proficient with C++; the process is rewarding, yet frustrating.
* Despite that, it lacks [[paradigms|OOP]] features like interfaces or properties.
* Any use of templates makes the compiler very slow and its error messages confusing.
* Essential features like strings and containers are implemented with template magic. Is it really better than compiler magic?
* In fact, compilers for most other [[languages]] are so much faster it's not even funny.
* Also, complete C89 compilers can be the size of a [[Lua]] interpreter.
Ultimately, C++ fulfills too many [[roles]], due to its popularity. That's not an easy problem to solve.
Conversely, the language gets a bad rap when it comes to safety. But I say C++ programmers are keenly aware that they're handling an industrial power tool. Hand guards can't help if you hold the tool wrong in the first place or try to pull off stunts.
Lua
[Lua](https://www.lua.org/) is mostly known as the world's most popular scripting language for games, but it's so much more.
- It's one of the fastest in its class, especially with the [LuaJIT](https://luajit.org/) interpreter.
- Interpreters are compact and widely available.
- It has a very clean syntax reminiscent of [[Pascal]].
- It's truly small, with few concepts to remember and a minimal standard library.
- The standard library still includes usable regular expressions.
Commonly invoked disadvantages include:
- Tables pull double duty as lists and dictionaries.
- Tables can't store nil values either way.
- Sequential tables count from 1 rather than the more common zero.
- The object system is prototype-based and can be clunky.
The main community hub appears to be [lua-users](http://lua-users.org/), but it's poorly maintained.
## Quirks
- All functions in the `strings` library can also be invoked as methods on string variables, but others can't be.
- You can't call a method on a literal, i.e. `"hello":len()`; it has to be a variable.
- Strings can't be indexed with square brackets like tables. You can try, but it always returns `nil`; use `strings.sub` instead.
- At the REPL, each line you enter is a separate chunk. If you declare a name with `local`, it won't be visible in the rest of the session.
- To display the value of an expression, you can use `return`, or shorten it to `=`.
## Implementations
Lua has multiple implementations with reasonable compatibility. The [Fennel wiki](https://dev.fennel-lang.org/) lists a bunch of them. The original implementation is generally preferred; most Linux distributions carry all versions from 5.1 to 5.4, plus [LuaJIT](https://luajit.org/), with one of them being default.
## Related languages
[Fennel](https://fennel-lang.org/) is a [[Lisp]] dialect that sits on top of Lua. It's small, fast and easy to set up or distribute, unlike similar projects for other languages.
[Nelua](https://nelua.io/) is a statically typed dialect with some interesting ideas picked up from [[Zig]], that compiles to native binaries via C.
Vala
Since people always ask: [Vala](https://vala.dev/) is a language from the Gnome project, like C# but for the GObject runtime.
The compiler is lightweight and widely available, but very slowly developed. The language is usable and can yield results quickly. Documentation is helpful enough too. A few popular apps are made with it.
In fact Vala seems designed for rapid application development in non-critical domains like desktop utilities. See below for reliability issues.
## Language
* Vala syntax is clean, but verbose; there's no typedef
* No explicit imports, and (experimentally) no need for a main function
* There's keyword stropping if you want it: a rare feature
* It also has advanced features like nullable types and contract programming
* Strings, dynamic arrays and slices are built into the language
* Vala is more than a thin layer over C and GLib2
* In fact you can use Vala without GObject and GLib2
* But if you want them, they can be pretty useful
* The object model includes rare features like signals
* The language is quirky and fussy, and the compiler buggy
* There's a kind of exception system, but they're managed!
* Killer feature: it comes packaged with bindings to dozens of C libraries
* On the minus side, it seems tricky to make your own bindings
## Compiler
* Compilation speed is very high
* The `vala` interpreter can run a program on the fly
* The compiler also supports a second language called Genie, inspired from [[Python]]
* Genie is less complete, less documented, and doesn't work in the interpreter
* Generated binaries are 40% smaller but also 40% slower than in [[C++]]
* With recent GCC versions, non-trivial code compiles with numerous C warnings
* The docs say to just ignore them, because Vala knows better
## Gotchas
* Vala uses reference counting without cycle detection; use with caution
* Hint: learn to work with weak and unowned references early on
* Vala's (or rather glib's) `malloc` deliberately crashes the program on failure instead of returning an error value that can be checked
Zig
After trying several times to figure out the [Zig programming language](https://ziglang.org/), I finally have a handle on things. Still seems noisy to me, but now it makes sense for a change. To wit:
* The "@" sigil at the start of some identifiers is simply a convention marking them as builtins (it also has other uses in the language).
* The use of `const` to declare [[types]] is because types in Zig are first-class values; `struct` simply returns an anonymous type, so you can assign it to a constant.
* Importing modules works on the exact same principle, too.
In fact, first-class types (along with `comptime` values) allow for generics without needing the *concept* of generics explicitly in the language. If only they'd have added exceptions instead of increasingly convoluted methods to handle error return types.
Zig seemed to have interesting ideas at first. I liked its creator's attitude and [[philosophy]], too. But now I see it's yet another language that doesn't trust the programmer and seeks to achieve "correct" code through restrictions instead of facilitating expression. For a better language with similar ideas, try Nelua.
D
Like its name suggests, the [D programming language](https://dlang.org/) once aimed to supplant [[C++]]. It ended up a testbed for some of its modern features instead.
- otherwise not widely used by itself, but well-supported by editors and build systems
- at least one semi-popular project is made with D
- an active [community](https://forum.dlang.org/) exists
- the creator of D can often be found posting there!
- as of 2024 there's ongoing [controversy](https://dpldocs.info/this-week-in-arsd/Blog.Posted_2024_01_01.html) about project governance
- despite that the language and ecosystem seem healthy enough
- huge programming language, with a hairy standard library
- compilers use considerable amounts of CPU and RAM
- the sheer size of the standard library cancels out compiler speed
- claims to be pragmatic, but it's made by two computer scientists
- encourages the programmer to write unit tests over working code
- the [arsd library](https://arsd-official.dpldocs.info/arsd.html) deals with more practical tasks
- there are three compilers using a common front-end
- compatibility between implementations is randomly broken
- as of DMD v2.111.0 executable size starts from 900KB for a "hello, world" but increases quickly
## Specifics
- immediately familiar language to anyone who's used C++ or Java
- allows for point-free programming and domain-specific [[languages]]
- garbage-collected language with built-in dynamic arrays and hash tables
- the garbage collector and other extras can be disabled
- has modern [[paradigms|OOP]] conveniences such as interfaces, mixins or properties
- offers a dedicated mechanism for defining recursive [[types]]
- supports contract programming and adding runnable examples to generated documentation
Nim
[Nim](https://nim-lang.org/) is like the love child of [[Pascal]] and [[Python]], made in the future. The type system in particular bears a striking resemblance to the former's. That's a good thing. Other notable features include:
* expression statements
* uniform function call syntax (same as in [[D]])
* keyword and operator stropping
All these combine into a language designed to let programmers express themselves.
## Notes
General:
* Highly portable compiler, with good support for cross-compilation.
* Relatively small package as modern [[languages]] go.
* Creates executables not much bigger than g++, except faster and lighter.
Language:
- Dynamic arrays are built-in; dictionaries are in the standard library.
- The same module can be used as a library or a main program.
Ada
According to a famous saying, there's no point learning a new programming language if it doesn't change the way you think. Ada qualifies in a way few others do.
Upsides:
- it's a hugely expressive language with an incredibly rich type system;
- it's as clear as it gets: package specifications basically read like English;
- it offers holistic safety features without restricting the programmer.
Downsides:
- the language is huge; it takes reading several manuals to start getting a feel for it;
- even then it's a lot to remember and make sense of;
- the premier open source compiler is developed by a commercial entity;
Also, the community mainly gathers in chatrooms, subreddits and a Usenet group. There doesn't seem to be a central wiki and/or forum like other [[languages]] have.
Fortran
It's refreshing to find out in 2024 that classic programming languages aren't just still in use, but continue to be actively improved, and in some ways are better designed than industry workhorses. One pleasant surprise was Fortran:
- It's a practical, easy to learn language with useful features built in.
- Source code can be organized very flexibly.
- Memory is automatically managed to a degree (not everywhere).
- The compiler is blazing fast.
- A [thriving community](https://fortran-lang.org/) is out there having a blast.
The issues mostly become apparent when writing actual code:
- String support is much better than in C, but still clunky.
- The syntax is verbose and sometimes fussy.
- Gotcha: `if` and `while` require parentheses like in C.
- ~~The language has many modern features, but no enums!~~
- Actually they sort-of exist in a limited form.
Fortran is definitely not for everyone, or every app. But it may be worth knowing, and sometimes even using.
## Gotchas
There's a difference between `character(*)` and `character(:)` – they can't be used interchangeably:
- `len=*` means assumed length; it works with constants and dummy arguments;
- `len=:` means deferred length, and goes together with `allocatable` strings.
Lisp
Lisp is a whole family of languages nowadays, but the name by itself often refers to one specific dialect. The [CLiki](https://www.cliki.net/) website kind of covers both uses, despite the name.
## Common Lisp
[Common Lisp](https://lisp-lang.org/) is supposed to be more practical than [[Scheme]], but it depends on your definition of practicality.
The good:
- thousands of built-in functions
- high compatibility between various implementations
- old, well-understood standard that got by without an update for 30 years
The bad:
- editor support is at best spotty outside of Emacs and (Neo)Vim
- Geany has decent syntax highlighting, but no other support
- somehow, third-party libraries are still needed for pretty much anything
The ugly:
- differences between implementations are the death of a thousand cuts
- whose bright idea was to name this function `(terpri)`?!
Also, one gets the impression that popular implementations are designed for the kind of workflow where you load a library at the interactive prompt and call functions manually, as opposed to running an app.
### Implementations
- [SBCL](https://www.sbcl.org/) is the most widely recommended and supported nowadays; medium-sized;
- compiles source code on the fly, even at the interactive prompt;
- can make stand-alone executables, but they're huge.
- [ECL](https://ecl.common-lisp.dev/) is likely the lightest Common Lisp system out there; also well-regarded;
- can optionally compile to native code;
- resulting executables depend on the installed runtime.
Note: this is no different from [[Python]] requiring a hard-to-amortize runtime. You can still distribute your apps as source code if your use case allows for it.
Scheme
Generally considered a [[Lisp]] dialect, [Scheme](https://www.scheme.org/) is itself a family of dialects. The numerous existing implementations differ wildly, insofar as:
- what parts of each standard they support, from R4RS to R7RS;
- what [SRFIs](https://srfi.schemers.org/) they support, either included or downloadable;
- what module system, object system and so on they have built in;
- which flavor of macros and/or records they support, if any;
- whether it's a compiler, interpreter, web-based or designed for embedding.
Potential ways to mitigate these issues to a degree:
- pick an implementation and stick with it
- use the standards for guidance and watch for the small details:
- TinyScheme supports string ports
- BiwaScheme accepts square brackets
- most take C-style escapes in string literals
See also: [your-first-implementation](http://community.schemewiki.org/?your-first-implementation) on the Community Scheme Wiki.
## Implementations
Shopping around for a Scheme to use feels like being a kid in a toy store. That said, a few stand out for various reasons:
- [Chicken Scheme](https://call-cc.org/) is popular and widely available, but idiosyncratic
- it's reasonably small in its class
- it has useful features and downloadable extensions
- it comes with an easy to use compiler
- a static "hello, world" can start from 1.1MB, same as in Go
- beware of differences from the interpreter
- [BiwaScheme](https://www.biwascheme.org/): R7RS in Javascript!
- on the small side for a modern JS library; easy to embed anywhere
- [TinyScheme](https://tinyscheme.sourceforge.net/): a still-popular classic
- incomplete R5RS with minor extensions; about as pure as it gets
- tiny C implementation, trivial to build and embed or run as-is
## Notes
In Scheme it's a natural idiom to read S-expressions from a file and interpret them however you like; for example as statements in a language you just made up.
Prolog
Prolog has surprising similarities to [[Lisp]], such as metaprogramming, homoiconic syntax and extensive use of lists.
Prolog is however much more abstract; implementations made for actual use have to make extensive additions to the standard. That said, they often try to stay compatible with each other.
While good introductions to the language exist, for example on [WikiBooks](https://en.wikibooks.org/wiki/Prolog), it's harder to find practical examples, even in places like [Rosetta Code](https://rosettacode.org/wiki/Category:Prolog). A highly regarded book appears to be [The Power of Prolog](https://www.metalevel.at/prolog).
## Implementations
There's a good handful of open source implementations in active development (plus a few proprietary ones).
* [SWI-Prolog](https://www.swi-prolog.org/) is a medium-size bytecode interpreter with many additional and nonstandard features. It has a GUI library but seems mostly focused on web development.
* It's the premier implementation these days and hosts the _de facto_ Prolog community.
* [GNU Prolog](http://www.gprolog.org/) is a small native code compiler with fairly conservative [[design]] and fewer things to learn upfront.
* It requires a C compiler, preferably GCC.
* The foreign function interface is very easy to use.
* [Tau Prolog](http://tau-prolog.org/) is a lightweight, modular Javascript implementation. It's standard-compliant and easy to run in a browser even on mobile devices.
Several other implementations have online playgrounds, such as [Ciao](https://ciao-lang.org/) and [Scryer Prolog](https://www.scryer.pl/).
## Example
A slightly less trivial "hello, world" (works in SWI-Prolog and Gnu Prolog):
```prolog
read_line(Line) :- read_line(Line, []).
read_line(Line, Init) :- peek_char('\n'), reverse(Line, Init).
read_line(Line, Init) :- get_code(C), read_line(Line, [C|Init]).
greet :- format("Hi! What's your name? ", []),
read_line(N), format("Nice to meet you, ~s!~n", [N]).
:- initialization(greet).
```
(Much like R5RS [[Scheme]], ISO standard Prolog lacks a built-in way to read an entire line of text from standard input; luckily it's not hard to do.)
## Gotchas
Most Prolog systems use a module facility; the predicates are portable, but which features are in libraries varies. For example the following directives are necessary in Scryer and Tau Prolog, but not others:
```
:- use_module(library(lists)).
:- use_module(library(format)).
```
Actually SWI-Prolog has a `lists` library module too, but can autoload it.
---
* In most systems, the `~s` format field takes either characters, codes or an atom; in Gnu Prolog there's a separate `~S` field for characters, and yet another for atoms.
* Beware that I/O backtracks! A cut after (say) handling input solves the problem, but is frowned upon in certain circles.
Perl
Perl may be the most misunderstood programming language. People complain it's cryptic... but you have the freedom to write crystal-clear code. All it takes is self-discipline. People complain it's dead, but they've been saying so since the early Oughts. If you look closely, it's everywhere, humbly getting work done. The aptly named [PerlMonks](https://perlmonks.org/) community is testimony to that.
I love Larry Wall's [postmodernism](http://www.wall.org/~larry/pm.html). It's a good [[philosophy]] to have. Frankly more programming language [[design]] should involve an [actual linguist](http://www.wall.org/~larry/natural.html). Perl's famous motto "there's more than one way to do it" is a rarity nowadays, when every other language seems made to constrain the programmer.
That said, Perl is the kind of language you have to *get*, like [[Scheme]] or Forth. It can take time and persistence. A few hints:
- Become familiar with the modules that ship with Perl (it's not really a standard library). There are some pleasant surprises in there.
- Use the `Tcl` module from [CPAN](https://metacpan.org/dist/Tcl) (packaged in Debian as `libtcl-perl`) to access Tk. It's up to date for a change.
- ~~Tasks like trimming blanks off the ends of a string can only be done with regular expressions and the substitution operator.~~
- As of version 5.36 you can `use builtin 'trim';` but it's still experimental.
- Objects in Perl are actually super-simple. Tip: any number of packages can be declared in one source file, including "main", which is the default.
- POD markup is usually placed at the end of a source file, after the `\_\_END\_\_` marker.
Accepted wisdom has it that no programming language is inherently compiled or interpreted. But in Perl those two are so thoroughly intermingled, it's hard to imagine performing them at different times.
## Tips and tricks
How to use the [dialog](https://invisible-island.net/dialog/dialog.html) utility from Perl:
```perl
use warnings;
use strict;
use IPC::Open3 'open3';
use Symbol 'gensym';
system "dialog", "--msgbox", "Hello, world!", "0", "0";
my $pid = open3('<&STDIN', '>&STDOUT', my $dlgout = gensym,
"dialog", "--inputbox", "What's your name?", "0", "0");
waitpid($pid, 0);
print "Nice to meet you, ", <$dlgout>, "!\n";
close $dlgout;
```
Error checking elided for brevity; see also: `can_run()` in `IPC::Cmd`.
Pascal
Pascal was the second programming language I ever learned, after Basic. Moved on soon enough however and didn't return for many years. During this time, things had changed.
As of 2025, [Free Pascal](https://www.freepascal.org/) is one of two open source implementations I'm aware of that are still around. It's widely available, with an active community, but development has slowed down. There are some apps made with it floating around, like [view3dscene](https://castle-engine.io/castle-model-viewer) or [CudaText](https://cudatext.github.io/).
On Debian Linux, a minimal version can be installed with `sudo apt-get install fp-compiler`. For a blast from the past, try `fp-ide` instead: it sets up a Turbo Pascal clone with its own built-in compiler, that works but shows its age badly. So does the language itself, which has all the modern amenities, but it's painfully obvious how many of them are tacked on. Many have to be enabled with compiler switches. On the plus side:
- dynamic, resizable arrays are built into the language;
- there's a variety of string [[types]], half of them reference-counted;
- there's a limited, but perfectly usable `crt` unit in the runtime library;
- also variants and an impressive array of string functions;
- the `TStringList` class from the `classes` unit is a killer feature;
- the compiler produces static executables of surprisingly small size;
- however the `-XX` compiler option does nothing on Haiku.
The compiler is fast, and doesn't seem to slow down when generics are used. There are several kinds of collections; unfortunately those in `rtl-generics` compile with a lot of warnings.
Free Pascal is easier to get into than [[Ada]] and has better string support than [[Fortran]]. There are some gaps in the documentation, but that's understandable; it's best to use the wiki and reference manual together. The learning curve is no problem for an experienced programmer: I was able to start doing real work in a couple of days. It took a lot of reading however. This isn't like a Pascal from the 80s! See: [modern Object Pascal introduction for programmers](https://castle-engine.io/modern_pascal).
## Survival guide
- Most new code should probably use the `{$mode objFPC}` and `{$H+}` compiler directives.
- Some of it has to, for example anything that uses the `contnrs` unit.
- Familiarize yourself with the contents of the runtime library (a.k.a. RTL). There's a lot of useful stuff.
- Also with the Free Component Library, that supplements the RTL.
Awk
[Awk](http://www.awklang.org/) is the forgotten secret weapon of Unix-like operating systems. It's a [[roles|scripting language]] with maybe a dozen statements and two dozen functions; its power consists mostly in implicit behaviors. Awk can be used just like any other: `BEGIN { print "Hello, world!" }` works fine. But it also has regular expressions, command piping and record handling. All these features make it exceptionally good at processing text.
## Resources
There are two main implementations today:
- [mawk](https://invisible-island.net/mawk/) is a fast interpreter with minimal extensions from the original language, popular on lighter operating systems;
- [GNU Awk](https://www.gnu.org/software/gawk/) is a bigger implementation with serious extensions like networking functions and arrays of arrays;
But the original version from the 1980s has also been revived, and more exist, like [GoAWK](https://benhoyt.com/writings/goawk/). To learn either, you can use their respective manuals, or one of these:
- [An Awk Primer](https://en.wikibooks.org/wiki/An_Awk_Primer) on Wikibooks
- [The AWK Programming Language, Second Edition](https://awk.dev/)
The Alpine Linux wiki has a [page about Awk](https://wiki.alpinelinux.org/wiki/Awk) that includes a crash course and notes on different implementations.
## Notes
Awk is strongly reminiscent of JavaScript, from the way function arguments work, through the first-class regular expressions, to how arrays are implemented. But it also resembles [[Rexx]], at least in the way it handles file I/O and string concatenation.
Gawk supports Unicode, depending on system locale; mawk isn't supposed to, but its strings appear to be 8-bit transparent and will pass through whatever you give it.
Indexing of strings and arrays is 1-based in Awk, with the exception of command-line arguments.
GoAWK and (in newer versions) gawk can handle CSV files.
Standard Awk doesn't have a switch statement, but the above two implementations both do.
Rexx
Sometimes it's worth learning a programming language just to see how different things could have been in computing.
- A language off the beaten path, very much unlike others.
- A lot of thought went into it, and it shows.
- Has unique features like `parse` or the data stack / queue.
- Friendly language from the same school of thought as [[Awk]].
- Though from another milieu: mainframes rather than Unix.
- See the way file I/O works, in particular.
Rexx is one of those languages best used on-site, to solve specific problems, as opposed to making reusable software for publication.
## Notes
- Modularization is on par with line-number Basic: a program can be made of multiple scripts that `call` each other much like procedures.
- There's no Unicode support, but strings are 8-bit transparent: UTF-8 in source code is parsed without complaint and printed out as-is.
- The `%` and `//` operators have their meanings reversed compared to [[Python]].
### The good
- [Learning resources](https://rexxinfo.org/) are easily available.
- Here's a nice [introduction to Rexx](https://www.bracksco.com/personal/rexxintro.html) that's kept up to date.
- Regina (see below) also comes with an excellent, detailed manual.
- There are a handful of actively maintained implementations.
- [Regina](https://regina-rexx.sourceforge.io/) is small, portable and easy to build from source where needed.
- Useful built-in features:
- Precise decimal arithmetic by default.
- Easily work with external programs through `address`.
### The bad
- Most implementations are maintained by just one group: the [Rexx Language Association](https://www.rexxla.org/).
- Most documentation comes as PDFs and isn't easy to search or browse.
- Practical examples are hard to come by in the wild; there are few users outside the mainframe world.
- All communication takes place via mailing lists with no public archives.
- No built-in trig or other math beyond `abs`, `sign`, `trunc` or `min` / `max` and `random`.
### The ugly
- Compatibility between implementations is poor; they're more like different dialects.
- Working with procedures is quirky and can get cryptic.
## Example
Rexx doesn't have an interactive loop out of the box, but it's not hard to make one:
```rexx
#!/usr/bin/env rexx
/* Trivial interactive prompt for Rexx. */
signal on halt
signal on syntax
parse version v; say v; say ""
do while lines() > 0
call charout , "> "
interpret linein()
end
exit
halt:
say "Exiting on request"
exit
syntax:
call charout , "Error" rc ": "
say errortext(rc)
exit
```
The code isn't very robust: for one thing there's no recovering from errors. But it can be helpful.
(As of version 3.9.7, Regina has an interactive mode that's a little more robust.)
philosophy
One can learn a lot about a programming language from the way it handles the simple task of counting from 1 to 10: something that occurs more often than you'd think in real-world code!
- In [[Scheme]] I have to stop and think how to express that as a named `let`.
- In [[C++]] and the like I have to be careful how I write the loop condition.
- In [[Lisp]], remember that `dotimes` always counts from zero.
Contrast with [[Fortran]] or [[Pascal]], where you pretty much just tell the computer "count from 1 to 10 here". That's how you know they're made for human beings.
(Note: this isn't even about programming [[paradigms]]; it's simple stuff.)
## Quips
Hot take: a little verbosity in a programming language is good. It provides just enough friction to make you slow down and think of what you're doing.
---
Oh, you aim to create a hundred-year programming language? Cobol would like a word.
---
My first time trying to learn [[Perl]], I ended up doing PHP instead. The second attempt got me into [[Python]]. Third time it was [[Rexx]] of all things.
roles
Programming [[languages]] can be roughly placed into three categories by their intended role. While the division is far from clear-cut, conflict arises when a language optimized for one role is expected to perform another.
* System programming languages are used to build solid foundations, from operating systems to shells, tools and libraries. These languages need to provide general programming facilities like containers and algorithms.
* Application programming languages are needed to build on top of those foundations. Their standard libraries have to include more specific features like parsing command lines and CSV files, accessing networks or building user interfaces.
* Scripting languages are tricky because "scripting" can mean at least two things:
* Extension languages, meant to be embedded in an application and let users write plugins for it – [[Lua]] being the most famous;
* Glue languages, intended to make lower-level tools or libraries work together on solving a complex unanticipated task ([[Rexx]] is the ur-example here).
Obviously some scripting languages can perform in both roles, and arguably more besides, like [[Python]] is often used for application development. But they're seldom equally well-suited, and they need to be designed with purpose in any event.
types
Over the past fifteen years or so, discourse in the programming community went like this:
> "It's too hard to make desktop apps in C++. I know, let's use JS instead!"
>
> "Oh no, our apps are full of bugs now! It's due to weak types, you say? I know, let's add strong, static typing!"
>
> "There, sound software engineering principles have been restored. Oh no, what do you mean JS is as hard to use as C++ now?"
Funny how that drove a big push to modernize the hoary old Algol formula. As it turns out, compilers never needed explicit type declarations in most places, since type can be trivially inferred from an initializer. I did that in a toy compiler once. For fun.
On a related note: used to be that only compilers could afford to perform complex type checks, because they took time. Nowadays computers are fast. There are compilers that can compile themselves faster than the CPython VM starts up. Even sum types, once the province of research languages, have become a popular feature. Compiled languages with static typing can be used for scripting. PHP and [[Python]] have gradual typing (the latter via third-party tools). The lines are blurring, and fast.
There are languages with only one type, or even none at all. Hint: it's all in the operations you can perform on them. And those aren't always simple. You know how people make fun of JS or PHP for having a `===` operator? Ask a mathematician sometime how many different kinds of equality there are. Or count the equivalence predicates in [[Scheme]]. I mean it, look them up!
Type systems weren't supposed to be a safety net for dizzy programmers. Think building blocks, not prison bars.
paradigms
Programmers tend to have Very Strong Opinions about their tools. Imagine carpenters arguing over the relative merits of glue versus nails! That said, there are some things worth keeping in mind about different ways to structure code.
## Functional programming
Functional programming is controversial, perhaps because it's not clear where the line should be drawn. Someone even told me once that [[Lisp]] is an imperative language in disguise. Presumably that was because it has explicit control structures. I even believed it for a while, but that's not it. More recently I've learned about expression statements (i.e. having `if` return the last evaluated expression). Which, by the way, have existed since Algol 68!
Now imagine you don't have explicit `return`, `break` or `continue` statements at all, so every function *has* to be one big expression, even if that includes explicit loops. Actually you don't have to imagine it: the ML family of languages is like that, to quote one that isn't [[Scheme]]. Which happens to make them easily implementable with tree walkers (because you don't need goto under the hood), so that's very convenient.
Then again early return can be very useful, which is one reason why Scheme has continuations. That's why [[languages]] that support expression statements, such as [[Nim]], usually also have explicit ways to end a loop early.
Closures and first-class functions are another story.
## Object-oriented programming
After the excesses of the mid-1990s, OOP has gotten a bad rap. Now there are people who won't touch it with a ten-foot pole. Luckily most language designers disagree, and put in at least some support for it:
- [[Lua]] and JavaScript have prototypes
- [[Zig]] and Go kinda hold their nose and extend structures
- [[Perl]] leverages its package system (but also added classes recently)
Most other languages use classes, to the degree that some programmers believe they're synonymous with OOP. And then there are languages like Object [[Pascal]], that have at least two different systems, and let you choose.
design
So you want to design your own [[languages|programming language]]. Great! Worst case, you'll learn a lot, and have fun for years. A few remarks from my experience:
- Just use a [recursive descent parser](https://en.wikipedia.org/wiki/Recursive_descent_parser), or failing that [S-expressions](https://en.wikipedia.org/wiki/S-expression).
- Follow the "different things should look different" rule if you can; it helps both the parser and your users.
- Allow syntactic sugar but make it optional; your code will be much cleaner.
- If your parser is handcrafted – [as it should be](https://tiarkrompf.github.io/notes/?/just-write-the-parser/) – spelling out operators (and, or, not) can create ambiguity; use symbols, like in C.
- Parse your input language rigorously; be generous with error messages.
- There are few things worse than a program that silently fails.
- Providing context for errors takes more work. Don't sweat it, but do your best.
- Think about variable scope and implement it rigorously.
- You can't have good scope rules without variable declarations.
- Some languages make do without an explicit null value, but even in [[Scheme]] there are procedures that don't return anything. You're going to need some way to express the concept of "no meaningful value", at least internally.
- You want a boolean [[types|data type]] in your language; it will save you a bunch of headaches.
- Conversely, not all programming languages have to be math-oriented. [[Rexx]] only comes with half a dozen math functions; in Tcl, arithmetic expressions are an embedded language used with `expr`.
Last but not least, beware of the host (implementation) language influencing yours. That's not always a bad thing, but worth keeping in mind.