Edit, finish, and publish post on PL
Signed-off-by: Danila Fedorin <danila.fedorin@gmail.com>
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
---
|
||||
title: "Reasons to Love the Field of Programming Languages"
|
||||
date: 2025-12-06T18:08:24-08:00
|
||||
draft: true
|
||||
date: 2025-12-31
|
||||
tags: ["Programming Languages", "Compilers", "Type Systems"]
|
||||
---
|
||||
|
||||
@@ -18,6 +17,8 @@ theorists to developers to laypeople.
|
||||
So, in that spirit, I am writing this list as a non-exhaustive survey that holds
|
||||
the dual purpose of explaining my personal infatuation with PL, and providing
|
||||
others with ways to engage with PL that align with their existing interests.
|
||||
I try to provide rationale for each claim, but you can just read the reasons
|
||||
themselves and skip the rest.
|
||||
|
||||
My general thesis goes something like this: programming languages are a unique
|
||||
mix of the __inherently human and social__ and the __deeply mathematical__,
|
||||
@@ -169,7 +170,7 @@ They are rooted not only in pragmatic concerns of "what can I do with
|
||||
these transistors?", but in the deeper questions of "what can be done
|
||||
with a computer?".
|
||||
|
||||
* __Reason 7__: programming languages are built on foundations of computation,
|
||||
* __Reason 7__: general-purpose programming languages are built on foundations of computation,
|
||||
and wield the power to compute anything we consider "effectively computable at all".
|
||||
|
||||
Because of these mathematical beginnings, we have long had precise and powerful
|
||||
@@ -216,7 +217,8 @@ spite of Rice's theorem. Much of the time, too, we can do so in a way that is
|
||||
straightforward for humans to understand and machines to execute.
|
||||
|
||||
* __Reason 9__: in the face of the fundamentally impossible, type systems
|
||||
grant us confidence in our programs for surprisingly little conceptual cost.
|
||||
pragmatically grant us confidence in our programs for surprisingly little
|
||||
conceptual cost.
|
||||
|
||||
At first, type systems look like engineering formalisms. That
|
||||
may well be the original intention, but in our invention of type systems,
|
||||
@@ -251,7 +253,7 @@ I therefore present:
|
||||
|
||||
{{< details summary="Bonus meta-reason to love the mathy side of PL!" >}}
|
||||
In addition to the theoretical depth, I also find great enjoyment in the way that PL is practiced.
|
||||
Here more than elsewhere, the creativity and artfulness I've mentioned before come into
|
||||
Here more than elsewhere, creativity and artfulness come into
|
||||
play. In PL, [inference rules](https://en.wikipedia.org/wiki/Rule_of_inference) are a
|
||||
lingua franca through which the formalisms I've mentioned above are expressed
|
||||
and shared. They are such a central tool in the field that I've
|
||||
@@ -274,3 +276,140 @@ PL.
|
||||
for expressing its formalisms, which precisely highlights core concepts
|
||||
and leaves room for creative expression and elegance.
|
||||
{{< /details >}}
|
||||
|
||||
I know that mathematics is a polarizing subject. Often, I find myself
|
||||
torn between wanting precision and eschewing overzealous formalism. The
|
||||
cusp between the two is probably determined by my own tolerance for abstraction.
|
||||
Regardless of how much abstraction you are interested in learning about,
|
||||
PL has another dimension, close to the ground: more often than not, our languages
|
||||
need to execute on real hardware.
|
||||
|
||||
### Pragmatics of PL
|
||||
|
||||
Your perfectly-designed language can be completely useless if there is no
|
||||
way to
|
||||
{{< sidenote "right" "execute-note" "execute it" >}}
|
||||
Technically, there are language that don't care if you execute them at all.
|
||||
Many programs in theorem-proving languages like Agda and Rocq exist only
|
||||
to be type-checked. So, you could nitpick this claim; or, you could take
|
||||
it more generally: your language can be useless if there's no
|
||||
way to make it efficiently do what it's been made to do.
|
||||
{{< /sidenote >}} efficiently. Thus, the field of PL subsumes not only
|
||||
the theoretical foundations of languages and their human-centric design; it
|
||||
includes also their realization as software.
|
||||
|
||||
The overall point of this section is that there is much depth to the techniques
|
||||
involved in bringing a programming language to life. If you are a tinkerer
|
||||
or engineer at heart, you will never run out of avenues of exploration.
|
||||
The reasons are all framed from this perspective.
|
||||
|
||||
One fascinating aspect to programming languages is the "direction" from
|
||||
which they have grown. On one side, you have languages that came
|
||||
together from the need to control and describe hardware. I'd say that
|
||||
this is the case for C and C++, Fortran, and others. More often than not,
|
||||
these languages are compiled to machine code. Still subject to human
|
||||
constraints, these languages often evolve more user-facing features as time
|
||||
goes on. On the other side, you have languages developed to enable
|
||||
people to write software, later faced constraints of actually working
|
||||
efficiently. These are languages like Python, Ruby, and JavaScript. These
|
||||
languages are often interpreted (executed by a dedicated program), with
|
||||
techniques such as [just-in-time compilation](https://en.wikipedia.org/wiki/Just-in-time_compilation).
|
||||
There is no one-size-fits-all way to execute a language, and as a result,
|
||||
|
||||
* __Reason 11__: the techniques of executing programming languages are varied
|
||||
and rich. From compilation, to JIT, to interpretation, the field
|
||||
has many sub-disciplines, each with its own know-hows and tricks.
|
||||
|
||||
At the same time, someone whose goal is to actually develop a compiler
|
||||
likely doesn't want to develop everything from scratch. To do so would
|
||||
be a daunting task, especially if you want the compiler to run beyond
|
||||
the confines of a personal machine. CPU [architectures](https://en.wikipedia.org/wiki/Instruction_set_architecture)
|
||||
and operating system differences are hard for any individual to keep up with.
|
||||
Fortunately, we have a gargantuan ongoing effort in the field:
|
||||
the [LLVM Project](https://llvm.org/). LLVM spans numerous architectures
|
||||
and targets, and has become a common back-end for languages like C++
|
||||
(via [Clang](https://clang.llvm.org/get_started.html)), Swift, and Rust.
|
||||
LLVM helps share and distribute the load of keeping up with the ongoing
|
||||
march of architectures and OSes. It also provides a shared playground upon
|
||||
which to experiment with language implementations, optimizations, and more.
|
||||
|
||||
* __Reason 12__: large projects like LLVM enable language designers to
|
||||
lean on decades of precedent to develop a compiler for their language.
|
||||
|
||||
Though LLVM is powerful, it does not automatically grant languages implemented
|
||||
with it good performance. In fact, no other tool does. To make a language
|
||||
run fast requires a deep understanding of the language itself, the hardware
|
||||
upon which it runs, and the tools used to execute it. That is a big ask!
|
||||
Modern computers are extraordinarily complex. Techniques such as
|
||||
[out-of-order execution](https://en.wikipedia.org/wiki/Out-of-order_execution),
|
||||
[caching](https://en.wikipedia.org/wiki/Cache_(computing)#HARDWARE),
|
||||
and [speculative execution](https://en.wikipedia.org/wiki/Speculative_execution)
|
||||
are constantly at play. This means that any program is subject to hard-to-predict
|
||||
and often unintuitive effects. On top of that, depending on your language's
|
||||
capabilities, performance work can often entail working with additional
|
||||
hardware, such as GPUs and NICs, which have their own distinct performance
|
||||
characteristics. This applies both to compiled and interpreted languages.
|
||||
Therefore, I give you:
|
||||
|
||||
* __Reason 13__: improving the performance of a programming language is rife
|
||||
with opportunities to engage with low-level details of the hardware
|
||||
and operating system.
|
||||
|
||||
In the [mathematics section](#the-mathematics-of-pl), we talked about how constructing correct
|
||||
optimizations requires an understanding of the language's semantics. It
|
||||
was one of the practical uses for having a mathematical definition of a language.
|
||||
Reason 13 is where that comes in, but the synthesis is not automatic. In fact,
|
||||
a discipline sits in-between defining how a language behaves and
|
||||
optimizing programs: program analysis. Algorithms that analyze
|
||||
properties of programs such as [reaching definitions](https://en.wikipedia.org/wiki/Reaching_definition)
|
||||
enable optimizations such as [loop-invariant code motion](https://en.wikipedia.org/wiki/Loop-invariant_code_motion),
|
||||
which can have very significant performance impact. At the same time, for an
|
||||
analysis to be correct, it must be grounded in the program's mathematical
|
||||
semantics. There are many fascinating techniques in this discipline,
|
||||
including [ones that use lattice theory](https://cs.au.dk/~amoeller/spa/spa.pdf).
|
||||
|
||||
* __Reason 14__: the sub-discipline of program analysis serves as a grounded
|
||||
application of PL theory to PL practice, enabling numerous optimizations
|
||||
and transformations.
|
||||
|
||||
The programs your compiler generates are software, and, as we just saw,
|
||||
may need to be tweaked for performance. But the compiler and/or interpreter
|
||||
is itself a piece of software, and its own performance. Today's language
|
||||
implementations are subject to demands that hadn't been there historically.
|
||||
For instance, languages are used to provide [language servers](https://microsoft.github.io/language-server-protocol/)
|
||||
to enable editors to give users deeper insights into their code. Today,
|
||||
a language implementation may be called upon every keystroke to provide
|
||||
a typing user live updates. This has led to the introduction of
|
||||
techniques like the [query architecture](https://ollef.github.io/blog/posts/query-based-compilers.html)
|
||||
(see also [salsa](https://github.com/salsa-rs/salsa)) to avoid
|
||||
redundant work and re-used intermediate results. New language implementations
|
||||
like that of [Carbon](https://github.com/carbon-language/carbon-lang)
|
||||
are exploring alternative representations of programs in memory. In
|
||||
short,
|
||||
|
||||
* __Reason 15__: language implementations are themselves pieces of software,
|
||||
subject to unique constraints and requiring careful and innovative
|
||||
engineering.
|
||||
|
||||
### Conclusion
|
||||
|
||||
I've now given a tour of ways in which I found the PL field compelling,
|
||||
organized across three broad categories. There is just one more reason
|
||||
I'd like to share.
|
||||
|
||||
I was 16 years old when I got involved with the world of programming
|
||||
languages and compilers. Though I made efforts to learn about it through
|
||||
literature (the _Dragon Book_, and _Modern Compiler Design_), I simply
|
||||
didn't have the background to find these resources accessible. However, all
|
||||
was not lost. The PL community online has been, and still is, a vibrant and
|
||||
enthusiastic place. I have found it to be welcoming of folks with backgrounds
|
||||
spanning complete beginners and experts alike. Back then, it gave me
|
||||
accessible introductions to anything I wanted. Now, every week I see new
|
||||
articles go by that challenge my intuitions, teach me new things, or take PL
|
||||
ideas to absurd and humorous extremes. So, my final reason:
|
||||
|
||||
* __Reason 16__: the programming languages community is full of brilliant,
|
||||
kind, welcoming and enthusiastic people, who dedicate much of their
|
||||
time to spreading the joy of the field.
|
||||
|
||||
I ❤️ you.
|
||||
|
||||
Reference in New Issue
Block a user