Pick your battles: Choosing and Learning the “right” Programming Language

Comparison Metrics

  • Levels of abstraction

It can be helpful to look at today’s programming languages across 3 different levels — “build fast” programming languages used for prototyping applications as quickly as possible, “infrastructure” programming languages which help with performance-centric/use-case specific portions of your application and finally, “systems” programming languages useful for embedded hardware and use cases where you need absolute control of memory usage. Of course, lines along such levels tend to be a bit more blurred and muddy in reality, but it helps us form a mental map of how to approach problems! Recommendation: if you’re just starting out with developing applications, “Build fast” programming languages are highly recommended. They get you to see the end result of whatever you want to build, faster.

“Build fast” programming languages — you can lump languages like PHP, Javascript, Ruby and Python into this camp. Off the bat, they let you build much without getting in your way, the barrier to entry is way lower and their developer communities tend to be the largest. Their standard libraries, which come packaged with default language implementations for high-level problems (from networking and multithreading/multiprocessing along with synchronization across threads and processes to your typical operating system actions including interacting with the filesystem), also provide much out of the box – allowing you to get things done without having to look around or having to implement them yourself. Programming languages in this camp typically tend to be interpreted.

Implementing a multi-threaded HTTP requests in Python with static typing. Multi-threading takes advantage of interleaving the 3 task executions (let’s call them A, B and C tasks). While 1 task (say Task A) is doing some I/O-bound operation (and thus, is not doing any computation-bound work) other tasks (Tasks B and C) execute (yielding of course, when they themselves also hit some I/O-bound operation). All of the above imports are standard library offerings in Python 3 — no external dependencies/libraries!

“Infrastructure” programming languages — you can lump JVM-based languages (Java, Kotlin, Scala, Clojure, etc) as well as binary-producing languages like GoLang, Swift and Haskell into this camp. These languages tend to target performance in exchange for conveniences (strong typing, less components out of the box from the standard library, “heavier” verbosity, etc) without losing too much as well as impose rules to make getting things wrong, harder. They tend to be more manually tunable based on your execution environment (by allowing you to pass arguments that can affect runtime code performance). If you have a piece of your application that needs performance boost — especially if it’s running on the web, consider one of these for extra mileage.

The same multithreaded request problem implemented above in Python, implemented in Swift. As opposed to Python, Swift is compiled and will throw compile-time arguments if types don’t match as desired (allowing you to catch more mistakes during the code-writing process). DispatchGroup is what you might think of as a “barrier” synchronization — the completion handlers are placed on a queue for thread-based execution. We are simply waiting for all request executions to finish.

“Systems” programming languages — you can lump C, C++ and Rust into this camp. These languages provide you the most control for your application and explicit memory handling when need be. They also tend to work well with embedded devices (programmable micro controllers, computers with non-standard processor architectures) and hardware without much software support (e.g. accessing your car’s information through its OBD port). With the rise of WebAssembly, such “low-level” languages also prove useful for performing computation-intensive work in support of web applications.

  • Features and maturity

Syntax and data structures — languages serve as communication tools between a computer and a programmer. Take advantage of the language’s syntax. Know the most frequently used data structures in a language as well as their underlying implementation’s time complexities for insertions/deletions/modifications.

Haskell is a strict functional programming language that presents a ton of “pattern matching” features. It can inspect incoming data structures and act upon them if they fit the “demands”.

Runtime environment — be familiar with how your application “works” with respect to your computer. Does it need a language interpreter (the likes of Python, NodeJS, PHP)? Does it produce an architecture-specific binary (the likes of Swift and GoLang)? Does it use a mix of both — compiled and ran on some virtual machine (the likes of Java, Scala, Clojure)? Due to needs like these, learning and using Docker is highly recommended as well — you’ll learn a bunch about Linux administration along the way.

Libraries and maturity — Each language fits well for certain use cases based mostly on the kinds of projects their surrounding communities espouse. Java excels well for a lot of orchestration and network logistics-based requirements — database support through JDBC interface standardization and projects like those that fall under the Apache Foundation help Java serve that purpose. The same goes for Python and data analysis and statistics, as does Haskell with grammars, regular expressions and compilers. A language’s adoption rate and community size is also a great indicator of whether one should espouse a project under such a language — smaller language communities mean less help from the outside world when something breaks.

Garbage Collection

Garbage collection is the act of the program reclaiming memory space on its own, without the developer explicitly doing so (as you would in “systems languages” like C and Rust). In programming languages like Python, PHP and Swift, determining whether to deallocate objects or not are based on the count of references — the idea of reference counting. However, even while sharing this similarity, they differ on their respective implementations — specifically, with respect to how they handle classical memory leaks (objects with no useful references to them from the outside world but still prevent the garbage collector from cleaning them up!).

Python implements reference counting alongside a “stop-the-world” generational garbage collector — ”stop the world” as the garbage collector pauses program execution, kicks in and proceeds to do garbage collection, then resumes program execution and “generational” as the garbage collector mantains 3 separate “generations” — 3 sets of heaps. Generation 0 heap gets checked the most and contains the most “fresh” objects, followed by Generations 1 and 2.

Playing around with garbage collection in Python. Historically, Python has only had reference counting to determine whether to deallocate the memory used by an object or not.

Results of executing the above program with Python 2.7.12

PHP (since PHP5.3) implements a concurrent garbage collector alongside reference-counting, which runs alongside program execution if need be and is localized — not needing to traverse the entire memory space to search for cyclically referring objects by constructing a reference graph. Subgraphs which cannot be reached from the root can be safely eliminated.

Swift also uses reference counting but with no other garbage collection mechanism alongside it, leaving it up to the developer to ensure that objects which cyclically refer to each other are cleaned up through language primitives. Below, we demonstrate the use of a weak pointer — when an object’s “strong” reference count goes all the way down to 0, Person will be cleaned up (as it is only weakly referred to by Apartment). This allows Swift to add compile-time decisions as to when and where to garbage collect code, allowing it to save itself for having an actual runtime garbage collector.

Credits to https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html

There are lots of other garbage collection mechanisms implemented by other programming languages. They can widely affect the performance of an application depending on the use case so understand the memory model of your choice programming language for your use case!

read original article here