2022-11-25

opinions on programming languages

there are many programming languages - thousands. and the number keeps growing. informally, "programming language" often means syntax, semantics, compiler implementations, available libraries, extensions and the community all together or any of those. nevertheless, the basis of languages are syntax and semantics because the core utility of a language for a developer is to describe what a computer should do. the implementation and software written in it can be seen as just following from that. that is why i see syntax and semantics as the first most important aspect when judging a programming language. learning a language and getting to know its detailed semantics and associated environment well is a big investment because it takes many years of learning to become really good with it. people usually learn the languages the company they work with uses, they are told to learn, the ones that seem the most popular or those of which the marketing reached them, then stick with it for life and avoid considering alternatives. how languages become popular is an interesting topic by itself. often languages become popular because people write programs in it that become popular, but that does not really say much about the qualities of the language used. the initial choice often seems incidental, but the choice of the tool to use to solve the next problem often depends on the tool previously used: say someone spent two weeks learning language x and a new program is to be written - why use a new language y and require significantly more time in total for solving the task at hand, instead of re-using the existing knowledge and get it done somehow but quicker. the dependency grows. why ever use a different tool, when there is a known, more or less predictable path with existing tools. then there is fun in figuring things out: some things that are simple to solve with some languages might be more work in another, so far that there are issues that would never need to be solved if other tools were used, but that is usually not obvious, and it is exciting to find solutions. it does not matter much how long ago a programming language was invented. languages can evolve and old designs can theoretically prevail being superior. the state of implementations is important. with programming languages, it seems to be more likely for popular bad things to be improved with much effort until it works just well enough, than for people to switch to fundamentally better bases. for example an overcomplicated, inconsistent syntax might be more common because it is popular and an associated ecosystem exists

readability

this word seems to often mean "ease of thought" but i would like to note that many things sure are easy, if they have been trained for a few years. reading is a process that takes place for extracting information from a visual pattern, for example. this process can be divided into smaller processes, little actions that are necessary. for example having to look back to the beginning of a line to read the next one, which takes time, or recognizing something surrounded by a whitespace character as a separate thing, remembering a mix of agglutinated characters to be associated with a concept, et cetera. humans are not computers and costs may differ, but too much micro-effort over time might be stressful and limit intellectual capacity.

notation can allow different degrees of variability, where different formattings used make code structure look very different and harder to work with, while the code still has the exact same meaning. for example, whitespace is usually optional in many places and many different code formatting styles emerge from authors using whitespace differently

language ranking

while the following list does not go far beyond opinion, i only rate languages i have actually used for more than two work days

tier 1

scheme

syntax and semantics follow an ideal of unusually high pragmatic and auto-didactical efficiency. it is almost as simple as possible to learn, parse and translate.

  • it allows unobstructed functional abstraction that scales. any programming paradigm is possible and a matter of applying the fundamental building blocks - metaprogramming, anything goes
  • gives freedom that other languages take away: for example having a large range of allowed characters for custom identifiers. and it does not use much needless/noisy syntax except just round brackets - no semicolons, no commas, braces, brackets, colons
  • s-expression notation for scopes/lists and prefix notation is generic and consistent. the notation opens up freedom to focus on more important things. its homoiconic syntax allows for uncomplicated structural editing and the useful design of custom syntax (macros)
  • scheme uses a lot of round brackets - but notice that it is only round brackets and nothing else, for structuring. the regular self-similar style of the notation, combined with the usually relatively indicative and consistent plain english identifier naming gives an important didactic quality, an auto-didactic one, relevant each time when the code is read again. programming is often about managing complexity. having less superfluousness in a language, syntactically and semantically, reduces the complexity necessary to be managed. i would argue that the complexity created through the use of extra character based syntactic patterns grows not linear but even faster
  • scheme has standards documents that formally specify the language, which is an unusual thing considering that many other languages are implemented without a plan so to say, sometimes in an ad-hoc way where only the hardcoded implementation defines how the language is supposed to work
  • scheme allows me to create more with much fewer errors per time compared to other languages
  • side note: the lisp curse

c

some inherent consistency/simplicity, probably from "principle of least suprise"; low-level with a high degree of freedom, for example with memory use. its features usually have a fairly obvious implementation, but there is undefined behaviour or unspecified semantics that compiler implementations handle differently. has static typing, manual memory management and direct memory access. compilers to create highly optimised machine code for a lot of different platforms available. lacks features like a basic module (dependency) system and adequate binding scoping, memory ownership semantics, hygienic macros and other important things which makes it frankly tedious/dangerous in use but i do not see a better alternative at the moment

sc

c written with s-expressions

tier 2

  • coffeescript: javascript without the clutter. smart indent-based syntax, can use every javascript library, nodejs compatible. my most favorite syntax after schemes. it is smart with reducing ambiguity and making well recognizable patterns out of a small set of different ones. the syntax is also useful for configuration files, as it is simpler and more intuitive than for example yaml, ini, json, xml
  • sescript: javascript written with s-expressions. the compiled output is almost as if written in javascript directly

tier 3

  • javascript: quite expressive, but noisy syntactically (semicolons, which are optional but commonly used nevertheless, c-style, square/curly/round brackets mix, high formatting variability). large number of syntactically different ways to archieve similar things (object literals, functions that return objects, functions called with new, classes. async processing with async/await / explicit promise objects / with or without promise reject and catch handling, continuation passing style with error as the first argument). implicit type conversions, truthyness and falsyness, implicit global scope when not using var keyword. questionable, unusual async evaluation scheme. with having first class functions it bears some similarity to scheme
  • ruby: relatively consistent design. flexible syntax as in the parser can tolerate a lot. specialised for object-oriented programming (everything is an object). other strengths are one-liner scripts, string processing, metaprogramming using strings, reflection. invented as a successor to python, perl and smalltalk. uses begin/end keywords instead of indent syntax for scopes unfortunately
  • python: quite similar to ruby, in some ways cleaner and in some ways less clean, older. pro: indent syntax, good selection of scientific libraries, con: more irregularities, exceptional elements, limits and special character patterns (self, conventional underscore prefixes, everything is not an object, some cryptic forms)
  • clojure: more similar to common-lisp than to scheme. gets many things mostly right, like hashtable literals or preferring immutable data-structures. runs on and compiles for the multi-platform java virtual machine. so it is basically java and can use every java library. eclipse license
  • php: relatively predictable outcomes. big and useful intergrated library, which is one of the biggest benefits since it comes compiled with many features. since version seven it has also become quite fast. requires dollar signs for each local variable, particularly irregular naming scheme and syntax and generally lacky design, noisy (semicolons, dollar signs, backslashes in namespaces, what about parallelism, lots of for loops, having to check with isset before accessing hash entry, emits notice when trying to access inexistant hash entry), object-orientation features, more complex scoping rules, implicit type conversions, often no warnings when a variable used is not defined, no (good) first class functions. often needs more comments than code. javascript solutions can easily be nicer and more powerful. there is a site that documents some parts that are considered bad: phpsadness

not recommended

  • bash: while shell is very useful because it is the language on many command-lines, and is fine for small scripts, everything bigger than 30 lines... the syntax is a cryptic hodgepodge. there is so much formatting variability, and the keywords and built-in binding names are too meaningless and chaotic (if/fi, variable substitution and quoting, composition, array access). posix shell should be preferred to bash/zsh and others for portability
  • visual basic: superceeded by for example ruby. historic misfeatures and missing features (characters, error handling). rubys syntax is in many ways similar
  • common-lisp: total bloat in comparison to scheme. design and naming scheme is far less consistent and particularly arbitrary (defun, defvar, progn) with few english language words
  • java: business bloat. often taught in schools. specialised for object-orientation (files are classes, classes required). verbosity and repetition is the tenet here. uppercase characters and camelcase that hinders constructive naming. has a vm with decently optimised performance and works on many different platforms (benefit). tends to be written with more comment lines than code. it is self-similar in the sense that third-party libraries are like the core features: there are always many variants to choose from, but often none are good. you will probably rewrite basic stuff regularly, and have to rewrite it, because there are no features to abstract it, and invent new patterns to pass variable/class/method references around and generally just have to write a lot and create a big files with few things said
  • perl: a bit crazy (context sensitive expressions, dynamic scoping, shifting arguments, variable prefixes, scalar context, blessing, "my", 1; at the end of modules, proliferation of operators, difficult to impossible to install modules stored with the project, ...). its syntax is relatively easy to use and the interpreter forgiving but the code is still odd. you want to learn this stuff unnecessarily? it is kind of esoteric. bash is similar. everything that perl is deemed good for ruby can do better. but the community is still large and there are lots of modules, some well maintained
  • c++: not the successor of c. the extensions that c++ makes to c are for the most part unnecessary, overcomplicated or seriously questionable (object-orientation, exception handling). implicit type conversions and helper libraries can make it seem like a higher-level language. stl, templates and a hierarchical type system (think structs that can inherit from each other) are often quoted as the biggest benefits
  • c#: is non-free - owned and developed by the company microsoft for its closed source proprietary windows platform. has some focus on object-orientation. much more like java without a runtime than c. internet searches for c lead to c# results being included, and like c++ it falsly suggests being an official successor to the c language - deceptive marketing
  • objective-c: non-free - owned by the company apple and is explicitly and only targeted to its closed source proprietary ios and osx operating systems. unusual syntax that uses keyword arguments a lot (which i think is a good thing)
  • swift: successor to objective-c, same issues
  • haskell: functional perl. resembles traditional mathematical notation (in a way). over 700M install size. more functional than other languages and lazily typed (which is not necessarily better). cryptic code with lots of symbols. can not be understood intuitively by knowing a few patterns, not homoiconic. i would guess a lot of work to write a parser for. its main package manager is the worst i have ever used, as it was slow and got to irreversible corrupted stages. the language is perhaps still better than many other languages because of academic grounding

neutral

  • lua: may be used in programs as an extension language. similar to ruby with less features. supposedly small and easy to integrate
  • scala: functional features, c-style syntax, based on java and the java virtual machine like clojure
  • emacs-lisp: works only in emacs. it is not bad, but there could be better

other

  • go: garbage collection, cross platform compilation. no exceptions and instead error handling with multiple values
  • rust: low-level, no garbage collection, help with memory safety by introducing explicit memory ownership semantics, features for embedded programming. no runtime and compiles to machine code. short small type names. 128 bit types. hygienic macros. semicolon required for every expression. said to be more in the vein of c++