In “Part 1 - Compilers and Type-Systems” and “Part 2 - Memory Management and Concurrency”, we covered key programming language and runtime concepts like compilation, run-times, data-types, memory-management, and concurrency. Here in part 3 we will look at each of the 8 selected programming languages, how they differ, which use-cases each excels at, and current adoption. I’ve picked the following 8 languages based both on adoption and personal experience working with them. I have categorized these languages into 3 broad groups: compiled low-level, compiled high-level, and interpreted. Each programming language is also classified into one or multiple paradigms: procedural, object-oriented, or functional. We will begin with compiled low-level languages, the first of the programming languages.
Low-Level Compiled Languages
C
C has a long history in computer science and is what most modern programming languages trace their lineage back to. The development of C aligns closely with that of the Unix operating system. C was developed and publicly released in the mid 1970s by computer science legend, Dennis Ritchie, with influence from the B programming language from computer science legend, Ken Thompson. This is the Ken Thompson also renowned for the Unix OS (along with Ritchie), UTF-8, grep, and Go. C is one of the most prominent programming languages in history, one which has had a tremendous impact on software development.
C is statically-typed with manual memory management, supports bitwise and pointer arithmetic, and is widely used across industries for systems programming. It is known for speed and stability. Most modern languages follow similar syntax to C and many compile to C. Objective-C, C++, and C# all came after C, yet C is still widely used.
- Paradigm(s): imperative, procedural
- Compilation: compiled ahead-of-time
- Type-system: static nominal typing, supports signed and unsigned integers, supports pointers
- Memory-management: manual memory allocation without garbage-collection, low-level memory access, bitwise and pointer arithmetic
- Concurrency: kernel-threads
- Multi-threading: no built-in support, tools/libs available
- Adoption: very high adoption, especially in systems programming for governments, banks, embedded systems; created in 1973 by Dennis RItchie, designed to encourage cross-platform programming
- Other notes: often uses header files; many languages compile and/or are implemented with C
Rust
Rust is a newer language, publicly released in 2015 by Mozilla. Since 2017, Rust has seen increasing industry adoption as a memory-safe replacement for C/C++ system applications. Adopters include Amazon, Firefox, Intel, Discord, CloudFlare, Microsoft, and Facebook. For example, the Firecracker microvm developed by AWS for serverless Lambdas and Fargate is built using Rust. The common CURL library has started supporting Rust and Google’s Android OS is using Rust to rewrite their bluetooth stack. Rust has a strong ecosystem of libraries and the Cargo build-system and package-manager is powerful. Rust does not support null values, instead relying on optional types and requiring handling of the none-case, making it safer than C/C++. This eliminates null run-time errors common in many languages. As a low-level systems language Rust provides direct access to hardware and memory which provides flexibility in storing data on the stack or the heap. Rust does not run into the performance-drawbacks of a run-time garbage collector, instead using a build-time borrow-checker. This also avoids the error-prone and exploit-vulnerable downsides of manual memory allocation in languages like C/C++. The borrow-checker avoids the need for manual allocation of memory and subsequent freeing of memory; instead it keeps track of where data is used within the program and takes care of automatically freeing memory during build-time compilation.
Update (2021): In February 2021, the non-profit Rust Foundation was formed to support the continued maintenance and development of Rust sponsored by AWS, Google, Microsoft, and Mozilla.
- Paradigm(s): hybrid - imperative, procedural, object-oriented, functional; no class hierarchies; supports higher-order functions
- Compilation: compiled ahead-of-time
- Type-system: static nominal typing
- Memory-management: low-level memory access, manual memory allocation with static build-time borrow checker, does not permit null or dangling pointers
- Concurrency: many options for concurrency; often uses message-based concurrency; supports threads and shared-memory with safety-guarantees as well
- Multi-threading: yes, including support for parallel execution of threads across cores
- Adoption: open-sourced by Mozilla in 2015
- Other notes: Cargo is the build-system and package-manager
High-Level Compiled Languages
Java
Java became one of the most popular application programming languages in the world in the 2000s, used by the majority of enterprise software companies - Google, Amazon, Netflix, Airbnb, Spotify, and so forth. It was publicly released in 1996 by renowned computer scientist, James Gosling, with the intent to develop a language which may run anywhere on any computer. Unlike, C which at build-time compiles to machine-code for a specific CPU-architecture, Java compiles to intermediary Java bytecode which is then processed by a JIT at run-time within the Java Virtual Machine (JVM). Though Java applications are a bit slower and require more memory than C/C++ counterparts, not having to manage memory manually, built-in concurrency constructs, and its platform-independence resulted in massive adoption of Java through the late 90s and 2000s. Java is criticized for its verbosity, backwards-compatible implementation of generics requiring run-time type-erasure, lack of support for unsigned integers, high JVM memory requirements, and over-use of the thread-per-request shared-memory concurrency paradigm with each Java thread mapping one-to-one to a system thread. C# was later created by Microsoft in 2000 taking concepts from Java. Today, many modern business applications used everyday are built in Java.
- Paradigm(s): hybrid - imperative, object-oriented with inheritance, some functional support
- Compilation: compiled ahead-of-time to JVM bytecode paired with JIT
- Type-system: static nominal typing
- Memory-management: automated memory allocation with garbage-collection
- Concurrency: many options for concurrency; often uses explicit thread-pools mapped to kernel threads in conjunction with blocking operations; async and non-blocking operations available via NIO; futures and concurrent-collections also available
- Multi-threading: yes, including support for parallel execution of threads across cores
- Adoption: wide adoption in back-end business applications; released in 1995/96 by James Gosling
- Other notes: popular frameworks/libs/tools include Spring, Guice, Spark, Flink
Kotlin
Kotlin is a newer language, first released in 2016 by JetBrains, creator of the popular Intellij Java IDE. In 2019, Google adopted Kotlin as the official language for its Android operating system. It is also used by the likes of Amazon, Square, Uber, Netflix, and Pivotal. It was designed to improve on the earlier mentioned criticisms of Java with all the same benefits, the JVM, and easy interoperability.
Kotlin is fully interoperable with Java code making it extremely easy to adopt piecemeal and is less verbose, while compiling faster than alternatives like Scala. Scala, another JVM language interoperates with Java as well and focuses on functional programming (eg. currying, immutability). In contrast to Java’s typical focus on shared-memory with threads and locks and Scala’s on actor-based concurrency, Kotlin promotes CSP message-based concurrency using coroutines. These are a bit similar to goroutines in Go. Coroutines are much lighter than system threads, supporting multiplexing of thousands of coroutines on just a single system thread. Under-the-hood coroutines are implemented as finite-state-machines.
Kotlin supports several compilation targets: the Java Virtual Machine (JVM) for web services, JavaScript or WebAssembly for front-end browser-based applications, or native Android for mobile applications.
- Paradigm(s): hybrid - imperative, procedural, object-oriented with inheritance, functional; supports higher-order functions
- Compilation: compiled ahead-of-time to JVM bytecode paired with JIT; can also build to web assembly and Android
- Type-system: static nominal typing
- Memory-management: automated memory allocation with garbage-collection
- Concurrency: many options for concurrency including CSP-based coroutines
- Multi-threading: yes, including support for parallel execution of threads across cores
- Adoption: wide adoption for Android and increasingly back-end too; released in 2016
- Other notes: official language for Android applications
GoLang
Go is a newer language as well, released in 2012 by Google and designed by renowned computer scientist Ken Thompson (Unix, B/C, UTF-8). Go was motivated by distaste for C++ with improved safety and simplicity, though still syntactically similar to C. Go finds a nice middle ground between systems programming languages and higher-level application programming languages by offering garbage-collection, structural static typing, and CSP message-passing concurrency in goroutines, while compiling rapidly to a single binary executable.
Go has seen tremendous industry adoption since its release. Kubernetes and Docker are built in Go, as well as YouTube, Twitch, Dropbox, Terraform, and MongoDB. Kubernetes, the industry-adopted container-orchestrator open-sourced by Google has over 5 million lines of Go code and compiles in just a couple minutes - a far feat from many other languages.
- Paradigm(s): hybrid - imperative, object-oriented, functional; no class hierarchies; supports higher-order functions
- Compilation: compiled ahead-of-time to machine code
- Type-system: static structural typing
- Memory-management: automated memory allocation with garbage-collection
- Concurrency: CSP-based goroutines using buffered or unbuffered channels
- Multi-threading: yes, including support for parallel execution of threads across cores
- Adoption: increasing adoption in back-end web, tooling, and infrastructure, released in 2012
- Other notes: popular frameworks/libs/tools include Fiber and Gin
Interpreted Languages
Javascript/Typescript
Javascript is by far the most widely adopted language in the world, ahead of even Java, C, C++, or Python. Javascript is the core language of the web, with all major web browsers supporting it and over 97% of all websites using it. Backwards-compatibility is a major constraint for the JS language as it has to support many years and generations of web browsers. Though the base language has well-known deficiencies like loose-typing with bizarre type-coercions, newer releases (eg. ES6) and modern coding standards using static-analysis code-linting, along with the advent of the statically-typed Typescript, have eliminated many of these detractions.
Javascript leverages just-in-time compilation (JIT). Typescript compiles source at build-time into Javascript. The Javascript run-time can be either browser-based (Chrome V8, Firefox SpiderMonkey) or server-side using Node.js. Javascript is event-based, with a focus on asynchronous non-blocking calls. It supports functional programming paradigms with higher-order functions and currying. Javascript has prototypal inheritance; unlike most object-oriented languages where objects are created from classes, with prototypal inheritance objects are created by cloning or extending other objects.
To understand the vast adoption of Javascript, one only must look at the history of the Web. The first graphical web browser, Mosaic, was released in the early nineties, followed by the more widely-adopted Netscape in 1994. In 1995, Javascript was launched on Netscape to provide support for dynamic behavior on web pages after page-load. In other words, code executed in the browser. Javascript was originally intended as a scripting language sent over as small chunks of code with web pages. Javascript saw its rise alongside the rise of the modern World Wide Web in the late 1990s and through the 2000s as web pages became more interactive and dynamic in nature. In the 2010s, Single Page Applications (SPAs) shifted user-facing server-side application logic to the web through libraries like React, Backbone, and Angular. Javascript support splintered during the web browser wars with different browsers building their own Javascript engines with slightly different support. After Netscape, Microsoft’s Internet Explorer dominated, until Mozilla Firefox (SpiderMonkey) launched in 2004, and then Google Chrome (V8) in 2008.
ECMAScript is the standard specification for Javascript to ensure high interoperability across browsers. It was originally developed in 10 days for the Netscape web browser by Brendan Eich, along with the first Javascript engine. Since web browsers typically are single-threaded (though Web Workers get around this), the Javascript runtime achieves high concurrency using non-blocking IO operations and an event loop. These non-blocking operations either take a function callback as an argument or immediately return with a Promise, similar to Futures in other languages. Promises can be preferable over callbacks as they read much more cleanly; recent releases of ECMAscript (ES6) now support the even more preferable async/await syntax for asynchronous operations. Since 2010 with the launch of Node.js and NPM, Javascript is also used for server-side Javascript applications. Though often referred to as a single-threaded language, Node.js uses one thread for managing the event loop and the other for code execution.
Typescript 1.0 was released in 2014 as a statically typed superset of Javascript, designed by well known Danish computer scientist Anders Hejlsberg (Turbo Pascal, C#). In addition to static types, Typescript introduced object-oriented features similar to Java, C#, and C such as interfaces, enums, and generics. Typescript, along with new releases of ECMAScript, have addressed many prior complaints of JS. As such TS has seen rapid adoption both in the browser and server-side. On the server-side, Node.js applications quickly adopted Typescript to take advantage of static-typing. Node.js finds a good use case in non-safety-critical server applications featuring high I/O and low CPU computation. These server-side applications leverage asynchronous programming for concurrency and enjoy use of a single language across the full-stack of browser and server for quick development of applications. Node.js applications are typically deployed on low-cost single-CPU servers with wide horizontal-scaling to achieve high concurrency and parallelism at scale with low cost.
- Paradigm(s): hybrid - functional, imperative, object-oriented (TS); JS uses prototypal inheritance (eg. objects created from objects) which can emulate class-based classical inheritance; supports higher-order functions and currying
- Compilation: compiled (TS->JS), interpreted (JS)
- Type-system: static structural typing (TS), dynamic typing (JS)
- Memory-management: automated memory allocation with garbage-collection
- Concurrency: event-loop with non-blocking IO for high-concurrency within a single thread of code execution; lacks multithreading support for CPU-intensive tasks
- Multi-threading: no, under-the-hood uses two threads with just one for code execution
- Adoption: nearly 100% of browser-frontend code; sizable adoption of backend microservices requiring concurrency for high-IO use-cases (non-blocking), but poor for high-CPU use-cases as code execution is single-threaded; V8 was created in 2008, Node.js in 2010, ES6 in 2016; popular for supporting one language across entire tech-stack
- Other notes: popular frameworks/libs/tools include NPM, Express, React, Angular, Vue
Python
Python is an interpreted dynamically-typed high-level programming language popular for its readability and widely adopted data-science machine-learning libraries. These include: TensorFlow (Google), Pandas, Scikit, Keras, and PyTorch (Facebook). Python was developed by another well known Danish computer scientist, Guido van Rossum in 1991. The popular Python 2 was later released in 2000 and Python 3 in 2008. Unique to Python as compared to other languages listed is it uses whitespace for semantic code structure.
- Paradigm(s): hybrid - imperative, procedural, object-oriented, functional; supports higher-order functions
- Compilation: interpreted (default - CPython), JIT (PyPy)
- Type-system: dynamic typing
- Memory-management: automated memory allocation with garbage-collection
- Concurrency: multiple options for concurrency; explicit thread-pools mapped to kernel-threads or Python coroutines/async
- Multi-threading: no support for parallel execution of threads across cores with GIL enforcing one active thread per processor (CPython)
- Adoption: wide adoption in machine learning, some adoption in non-enterprise back-end web
- Other notes: popular frameworks/libs/tools include TensorFlow, Scikit, Pandas, PyTorch
Ruby
Ruby is an interpreted dynamically-typed programming language released in 1996 as an alternative to Python and Perl. It is implemented in C. Ruby was designed to be beginner-friendly with a unique syntax and where every line of code is evaluated as an expression. While Ruby is known for rapid development, valued by early-stage startups, Ruby is considered one of the slower languages when compared with the likes of Java and others. Ruby on Rails is the popular web framework for Ruby, providing an extensive set of functionality out-of-the-box.
- Paradigm(s): hybrid - imperative, procedural, object-oriented, functional; supports higher-order functions
- Compilation: interpreted
- Type-system: dynamic typing
- Memory-management: automated memory allocation with garbage-collection
- Concurrency: threads mapped to kernel-threads
- Multi-threading: no support for parallel execution of threads across cores with GIL enforcing one active thread per processor (CRuby)
- Adoption: once widely-adopted for non-enterprise backends especially with startups but seeing declining adoption
- Other notes: popular framework is Ruby on Rails