summaryrefslogtreecommitdiff
path: root/notes/260516-joke-rant.md
blob: a1c6f642c778a0b63832735504794ccb15621bb7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# Compilers are FAKE

_2026 May NOT_

Languages typically either

- follow the "interpreted language" mentality and at best add things
  like bytecode cache files or JIT as behind-the-scenes optimizations,
  or

- follow the "compiled language" mentality where code written in the
  language is a representation of the intended behaviour of an
  executable that will eventually be produced.

In the latter model, compile-time logic is often limited to primitive
pre-processors, declarative DSLs or otherwise limited subsystems (C++
templates, Zig comptime, etc.) and often even offloaded to an entirely
separate "build system" language.  In the former model, compilation is
swept under the rug like it's shameful dirt.

Why?

How about designing a language that embraces the reality of what it
means to "compile code": Run an executable with source code as input.
Sound familiar?  IT'S WHAT AN INTERPRETER DOES!  So, in my preferred
language:

- The "interpreter" includes APIs for explicitly compiling any of the
  currently defined functions, modules, etc. straight from their in
  memory representation (be it AST or bytecode) into native code or
  assembly snippets, with the ability to then "serialize" or assemble
  and link these to create object files or executables.
  
- The "compiler" is just a thin wrapper around these APIs, loading
  some source files that define functions and globals etc. (all of
  which is first interpreted) then compiling them and producing
  executables.
  
- The "top level" of any source file is naturally executed at compile
  time.  You get meta programming and a build system language "for
  free" since these kinds of features can simply be libraries.

Some lisp/scheme implementations already contain an interpreter and
compiler in one, exposing parts of the compiler through APIs, and
allow full compile time code execution via macros.  However, in my
experience, they still tend to embrace the traditional interpreted
vs. compiled mentality, which sometimes just makes things awkward.
For example, GNU Guile insists on automatically compiling everything
into a bytecode cache on the filesystem, unless you pass a CLI flag
disabling this behavior.  And if you write a Guile Scheme codebase,
you still typically use Autotools and Make to call `guild` to compile
the Scheme files.  Just why?  You have the full Guile runtime at your
disposal, at compile time...  I propose a change in mentality.

## Dial the schizophrenia to 11

Compilers are all FAKE.  They just pretend to be compilers.  All a
compiler is, is an interpreter for a language that pretends to be a
"compiled language" when in reality it's just an interpreted DSL for
declaring the desired behavior of an executable, which is produced
when the codebase written in this DSL is interpreted ("compiled") by
the interpreter ("compiler").

They have played us for absolute fools.

C is a declarative, interpreted DSL to describe executables.

GCC is an interpreter that runs C, a declarative DSL describing the
executable that GCC should shit out.