<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Programming languages · David Brandt</title>
  <link href="https://icelander.net/v1/programming-languages/feed.xml" rel="self"/>
  <link href="https://icelander.net/v1/programming-languages"/>
  <id>https://icelander.net/v1/programming-languages/feed.xml</id>
  <author><name>David Brandt</name></author>
  <updated>2026-11-03T00:00:00Z</updated>
  <entry>
    <title>One language, transpiled to all the others</title>
    <link href="https://icelander.net/v1/programming-languages/one-language-transpiled-to-all-the-others"/>
    <id>tag:icelander.net,2026:one-language-transpiled-to-all-the-others</id>
    <published>2026-11-03T00:00:00Z</published>
    <updated>2026-11-03T00:00:00Z</updated>
    <category term="programming-languages" label="Programming languages"/>
    <summary>Around 2001 I was tired of writing the same business rules in C, C++, Java, PL/SQL and Pascal, so I wanted to write them once and transpile them into code each environment would own. I called it LCD, never wrote a line of it down, and dropped it.</summary>
    <content type="html">&lt;p&gt;&lt;em&gt;LCD, the language I never wrote&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Thought 2001, written November 2026. Programming languages.&lt;/p&gt;&lt;p&gt;Around 2001 I was fed up with writing the same business rules over and over.&lt;/p&gt;
&lt;p&gt;They were never interesting rules. How an invoice is totalled. Which orders need someone to approve them. When a
discount applies. The kind of thing a business could explain in a paragraph and then spend a decade being slightly
wrong about. The trouble was not writing them. The trouble was writing them again, and again, because each execution
environment that needed the rule ran on a different technology stack.&lt;/p&gt;
&lt;p&gt;There would be a copy in C. Several copies in C++, because C++ was not one environment but several: MFC had its own
idea of what a string was, Qt had another, and plain C++ with the standard library had a third. There was Java on the
application server, and PL/SQL in the database, because the batch jobs and the reports ran next to the data. And
probably Pascal, since Delphi was what a lot of people built their clients in.&lt;/p&gt;
&lt;p&gt;Every copy was written by hand, sometimes by me and sometimes by someone else, in a different idiom, with different
defaults for rounding, for null, for what dividing two integers means. On the day they were written they agreed. From
then on they drifted. A fix in one copy was a bug report waiting to happen in all the others.&lt;/p&gt;
&lt;p&gt;What I wanted was obvious, or felt obvious: write the rule once, in one language, and have it turn up in every one of
those environments as code that belonged there.&lt;/p&gt;
&lt;p&gt;I called it LCD.&lt;/p&gt;
&lt;h2&gt;The day job&lt;/h2&gt;
&lt;p&gt;LCD came straight out of my day job.&lt;/p&gt;
&lt;p&gt;From 1998 to 2002 I worked at OZ, on iPulse, a communication and presence system: buddy lists, who is online,
messaging, routing. My part was the plumbing underneath, the RPC system. Marshalling code, proxies and stubs, parsers,
network streaming, and code generators, for several programming languages and environments at once. Most of it was in
C++, Perl and Java.&lt;/p&gt;
&lt;p&gt;A code generator for RPC is a machine for writing one thing once and getting it in several languages. You describe a
message, and out come a C++ struct, a Java class and whatever Perl wants instead, with the code that turns each of
them into bytes and back. The shape of the data is the same everywhere because nobody wrote it more than once.&lt;/p&gt;
&lt;p&gt;The RPC layer talked to many different contexts, and every one of them needed the same logic. The generators gave
them that logic without anyone writing it twice. If that worked for the RPC logic, it could work for the business
rules too.&lt;/p&gt;
&lt;h2&gt;The name&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Lowest common denominator&lt;/em&gt; is usually an insult. Here it was a specification.&lt;/p&gt;
&lt;p&gt;The language would contain what every target could express, and nothing else. If C++ and Java could both do
something, and do it the same way, it was in. If one of them couldn&#39;t, it was out, or it was given a meaning that both
of them could carry. And the same test would have to hold for every other language: Python, which was already around,
C#, which arrived the year after, then Go, TypeScript and Rust, and behind them the niche and the obsolete, the
languages that still quietly run a payroll somewhere. The name was meant to keep me honest: whatever LCD was, it was not going to be a
superset with a large runtime pretending to be every language at once.&lt;/p&gt;
&lt;h2&gt;Transpiled, obviously&lt;/h2&gt;
&lt;p&gt;I was pretty obsessed with transpiling it. Not compiling it to some common machine, not running it on a virtual
machine of its own: turning it into source code in each target&#39;s own language.&lt;/p&gt;
&lt;p&gt;That was not the fashionable answer in 2001. The fashionable answer was Java&#39;s: one runtime, everywhere, write once
and run anywhere. Oracle had even put a Java virtual machine inside the database. The PL/SQL kept coming anyway,
because the people who worked in the database wrote PL/SQL, and their tools, their debuggers and their habits all
assumed it.&lt;/p&gt;
&lt;p&gt;So the output had to be code that the people in each environment would own. A Java developer should open the
generated file and see Java, the kind a careful colleague might have written. A database developer should see a
PL/SQL package. Nobody downstream should have to know that LCD existed in order to read, debug or profile what it
produced.&lt;/p&gt;
&lt;p&gt;I had a less practical hope for it, too. Code formatters weren&#39;t in everyday use yet, and linters were a rarity out in
the boondocks of Reykjavík, so a code review could turn into a flamewar over braces, indentation and names. If every
environment got the code in its own style, why not every person? Write the code the way you like it, read it the way
you like it, and there is nothing left to argue about.&lt;/p&gt;
&lt;p&gt;Part 2 is about why this one choice turned out to shape almost everything else.&lt;/p&gt;
&lt;h2&gt;What I knew&lt;/h2&gt;
&lt;p&gt;Looking back at the list of languages I had used by then, I can see why I never believed in one denominator for
everything.&lt;/p&gt;
&lt;p&gt;I had written C++ for years, in more than one job, and Java and Perl in the one I had then. Along the way, at
school, at work and in between, I had spent time with COBOL, Ada, Prolog, Lisp, assembly, BASIC, Pascal and LPC, the
language the LPMud game servers ran on. Plus C, of course, and, because work demanded it, PL/SQL.&lt;/p&gt;
&lt;p&gt;If you take the common denominator of all of those, you get very little. Prolog has no assignment. COBOL, at the time,
had no functions you could define yourself. SQL has no steps at all, only sets. Assembly doesn&#39;t have types. A
language that worked in all of them could describe data and almost nothing else.&lt;/p&gt;
&lt;p&gt;I knew that. The interesting question was never whether one LCD could cover everything. It was how far one could get,
and with what.&lt;/p&gt;
&lt;h2&gt;What it was meant to be&lt;/h2&gt;
&lt;p&gt;LCD was imperative: variables, assignment, conditions, loops, functions and procedures, records. All the traditional
concepts, nothing exotic. Its memory was managed, which in those days meant one of two things: Java&#39;s garbage
collector, or reference counting in C++. It had the classical data types and the kind of standard library you would
expect to find anywhere: text, numbers, dates, collections.&lt;/p&gt;
&lt;p&gt;It was for business logic. It would not draw windows, open files or talk to the network. The program around it would
do that, and hand LCD what it needed to make a decision.&lt;/p&gt;
&lt;p&gt;And it was meant to be transpiled into C, C++ in its various frameworks, Java, and every other programming language,
past, present or future. Perl was on my mind as well, the way it was on everyone&#39;s mind in 2001: as the nightmare you
woke from at three in the morning, crying, in a cold sweat, having dreamt that you had to change somebody else&#39;s
regular expressions. LCD was going to let Perl sleep.&lt;/p&gt;
&lt;h2&gt;Why it stayed a dream&lt;/h2&gt;
&lt;p&gt;It never got further than that. No grammar, no transpiler, no decisions about how to close the gaps between targets:
their type systems, their memory models, their ideas of what a record or a list is. I didn&#39;t write anything down.&lt;/p&gt;
&lt;p&gt;I dropped it for an unglamorous reason. It wasn&#39;t something I wanted to spend my time on, and in those days it would
have taken a massive amount of time. One person, several transpilers, a runtime library for every target, and no way
to know whether any of it was right until all of it was finished.&lt;/p&gt;
&lt;h2&gt;Why write it down now&lt;/h2&gt;
&lt;p&gt;Because the idea never went away.&lt;/p&gt;
&lt;p&gt;It has kept coming back ever since, these days as a language I call Y. I have never seriously sat down and built it.
I have started it and shelved it more times than I can count. The oldest attempt that left anything in writing is
YAPL, from 2019, which early on described itself as a transpiler to &amp;quot;all machines&amp;quot;: LCD again, nearly twenty years
later, under a different name. The latest version of Y has put transpiling, and the features that languages have in
common, back in the spotlight, and that has had me reminiscing about LCD.&lt;/p&gt;
&lt;p&gt;The rest of this series works out what LCD would have had to be:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;One language, transpiled to all the others.&lt;/strong&gt; This part.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Levels of denominator.&lt;/strong&gt; Why one lowest common denominator was never going to be enough, and what to do instead.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Where the languages disagree.&lt;/strong&gt; Division, rounding, null, and the empty string that Oracle thinks is nothing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The cost, then and now.&lt;/strong&gt; Why it would have taken years, and why that reason is mostly gone.&lt;/li&gt;
&lt;/ol&gt;
</content>
  </entry>
  <entry>
    <title>Levels of denominator</title>
    <link href="https://icelander.net/v1/programming-languages/levels-of-denominator"/>
    <id>tag:icelander.net,2026:levels-of-denominator</id>
    <published>2026-11-03T00:00:00Z</published>
    <updated>2026-11-03T00:00:00Z</updated>
    <category term="programming-languages" label="Programming languages"/>
    <summary>The common denominator of C, Java, PL/SQL and SQL is almost nothing, so LCD couldn&#39;t be one language. It needed a ladder of levels, from plain data up to managed references, with each target carrying the levels it could.</summary>
    <content type="html">&lt;p&gt;&lt;em&gt;Why one lowest common denominator was never going to be enough&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Thought 2001, written November 2026. Programming languages.&lt;/p&gt;&lt;p&gt;I was not under the delusion that the same LCD would work with everything.&lt;/p&gt;
&lt;p&gt;PL/SQL makes the point by itself. It was on the list for one reason: the product I was working on used it. It is
Oracle&#39;s own proprietary language, and outside Oracle&#39;s databases nobody in their right mind writes it. It is SQL
with loops, records, collections and exceptions bolted on, so you can write procedural code in it if you must, but its
idea of data is rows. Nothing in it is shared the way objects are shared in Java: you assign
a record and you get a copy. Put PL/SQL in the same bucket as Java and C++, and the denominator shrinks to whatever
PL/SQL can do. Leave it out, and you have left out the database, which is where a lot of the rules had to run.&lt;/p&gt;
&lt;p&gt;What I thought instead was that there could be &lt;em&gt;levels&lt;/em&gt; of common denominator. I was mostly thinking about
imperative languages, and about whether the idea could spread further than that.&lt;/p&gt;
&lt;h2&gt;The ladder&lt;/h2&gt;
&lt;p&gt;Here are the levels. Each one is the common denominator of a smaller family of languages. Each
level contains the one below it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Level 0: data.&lt;/strong&gt; Booleans, integers, decimals, text, dates, enumerations, records, lists and maps. No null. This is
what my RPC code generators at OZ already did: describe a shape once, and get it in every language. Almost everything
can carry level 0, because it is just declarations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Level 1: calculations and decisions.&lt;/strong&gt; Functions of values, with no state and no loops: arithmetic, conditions,
choices, failures, and the pure parts of the standard library. This reaches further than you&#39;d think. Every imperative
language can do it. So can PL/SQL. So can an SQL query, as an expression. So can COBOL, with &lt;code&gt;COMPUTE&lt;/code&gt; and &lt;code&gt;EVALUATE&lt;/code&gt;.
So can Lisp, and so can Prolog, as a predicate that never backtracks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Level 2: structured imperative.&lt;/strong&gt; Local variables, assignment, loops, procedures, and records and collections passed
around by value. This is the procedural core: C, C++, Java, Pascal, Ada, PL/SQL&#39;s procedural half, the structured
dialects of BASIC, and COBOL within its limits. SQL drops out here, because SQL doesn&#39;t do steps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Level 3: managed references.&lt;/strong&gt; Values that are shared between owners and change in place, with their lifetime
managed by a garbage collector or by reference counting. Java, C++ with reference-counted handles, Delphi, Perl and
Lisp can do this. PL/SQL drops out here.&lt;/p&gt;
&lt;p&gt;That last level is the language I wanted: imperative, with managed memory. The levels below it are what you
get when you ask how much of it survives in environments that can&#39;t manage memory that way.&lt;/p&gt;
&lt;p&gt;A module would say which level it needs, and the transpiler would refuse anything above that. A target would say which
levels it can carry. A rule written at level 1 could then be used from level-3 Java code and from an SQL query in the
database alike. The discipline for whoever wrote the rule would be simple: use the lowest level that can say it, because
that is the level that reaches the most places. Most business rules are decisions and calculations. Most of them would
have lived low.&lt;/p&gt;
&lt;h2&gt;What a level 1 rule looks like&lt;/h2&gt;
&lt;p&gt;LCD never got a syntax, so the examples in this series borrow Y&#39;s.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;module pricing
level calculations


-- A price after a discount, to the cent.
--   why: The order screen, the invoice run and the month-end report must show the same price.
function discounted(amount is decimal(12, 2), rate is decimal(5, 4)) returns decimal(12, 2):
    return round_half_away_from_zero(amount * (1 - rate), places: 2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the database, a level 1 function doesn&#39;t need to be a function at all. It can be an expression in a query, and run
over every row at once, which is where the database earns its keep:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT order_id, ROUND(amount * (1 - rate), 2) AS discounted
FROM   order_lines;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In COBOL it becomes a small subprogram, with overflow reported through a status field:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cobol&quot;&gt;       PROCEDURE DIVISION USING AMOUNT RATE RESULT STATUS-CODE.
           MOVE 0 TO STATUS-CODE
           COMPUTE RESULT ROUNDED = AMOUNT * (1 - RATE)
               ON SIZE ERROR MOVE 1 TO STATUS-CODE
           END-COMPUTE
           EXIT PROGRAM.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The rounding mode in the LCD version is not an accident. Rounding half away from zero is what Oracle&#39;s &lt;code&gt;ROUND&lt;/code&gt; and
COBOL&#39;s &lt;code&gt;ROUNDED&lt;/code&gt; both do natively, so neither of them needs any help. Pick a different mode and you&#39;d need a library
in each of them to do it for you. Part 3 is largely about choices like that one.&lt;/p&gt;
&lt;h2&gt;Who carries what&lt;/h2&gt;
&lt;p&gt;This is roughly how the languages I knew in 2001 would have sorted themselves. A full mark means the language
carries the level natively. A half mark means it can, with a runtime library or within limits. A cross means it
can&#39;t.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;0 Data&lt;/th&gt;
&lt;th&gt;1 Calculations&lt;/th&gt;
&lt;th&gt;2 Structured&lt;/th&gt;
&lt;th&gt;3 References&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;◐&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;◐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C++, in each framework&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pascal (Delphi)&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;◐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ada&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;◐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PL/SQL&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQL&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;COBOL&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;◐&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BASIC&lt;/td&gt;
&lt;td&gt;◐&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;◐&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lisp&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prolog&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;◐&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;C gets a half mark at level 1 because it has no decimal type, so money needs a library. COBOL gets a half mark at
level 2 because its tables have fixed sizes and, in the COBOL of that time, nothing could call itself. Prolog gets one
because loops have to become recursion and assignment has to become new names.&lt;/p&gt;
&lt;h2&gt;Why transpiling forces the levels&lt;/h2&gt;
&lt;p&gt;This is where the obsession with transpiling comes back in.&lt;/p&gt;
&lt;p&gt;If LCD had been compiled, levels would hardly matter. A compiler can put any program on any machine, because in the
end every target is machine code. One denominator, the whole language, would have done.&lt;/p&gt;
&lt;p&gt;A transpiler can only produce what the target language can &lt;em&gt;say&lt;/em&gt;, and say readably. The common denominator that
matters is one of expression, not one of execution. And expression is exactly where language families differ: SQL can
say a calculation beautifully and a loop not at all, and Java can say almost anything but says a decimal calculation
in a way nobody would call beautiful.&lt;/p&gt;
&lt;p&gt;So the levels are not a compromise forced on the design. They fall straight out of the decision to transpile.&lt;/p&gt;
&lt;p&gt;The same decision shapes everything else. Transpiled code is code people own. It has to read as if a careful
programmer of that language wrote it, with its own types, its own loops and its own way of failing. Names have to
survive unchanged, so that a search for a rule finds every copy of it. Each generated function has to say where it
came from, and nobody edits it by hand. Whatever a target lacks, such as decimals in C, has to come from a small
library written in that target&#39;s own language and shipped as source beside the generated code. And the whole thing
has to fit into each environment&#39;s build as it already is: the C++ goes through the C++ build, the PL/SQL is installed
like any other package.&lt;/p&gt;
&lt;h2&gt;Spreading further&lt;/h2&gt;
&lt;p&gt;The other half of the thought was whether LCD could spread beyond the imperative family. Each direction would have
needed something different.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sets.&lt;/strong&gt; Level 1 already reaches into SQL, as an expression in a query. A level of set operations, such as filtering,
joining, grouping and summing lists of records, would sit beside level 2 rather than above it. It would transpile into
SQL and Lisp naturally, and into loops everywhere else.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Logic.&lt;/strong&gt; Level 1 maps onto Prolog predicates. What Prolog adds, searching and backtracking, has no counterpart
anywhere else, so it would stay out. The interesting direction is the other one: LCD&#39;s rules written out as Prolog
facts and clauses could be asked &lt;em&gt;why&lt;/em&gt; a decision came out the way it did.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lisp.&lt;/strong&gt; Lisp could carry every level, and its macros could be LCD&#39;s transpiler for Lisp. It is the one target where
LCD would have been a library rather than a code generator.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Assembly.&lt;/strong&gt; Assembly can run anything and explain nothing. LCD&#39;s whole promise was readable code in each
environment, so assembly would have been a compiler&#39;s job, never LCD&#39;s.&lt;/p&gt;
&lt;p&gt;Next, in part 3: what happens inside a level, when the languages that are supposed to agree simply don&#39;t.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Where the languages disagree</title>
    <link href="https://icelander.net/v1/programming-languages/where-the-languages-disagree"/>
    <id>tag:icelander.net,2026:where-the-languages-disagree</id>
    <published>2026-11-03T00:00:00Z</published>
    <updated>2026-11-03T00:00:00Z</updated>
    <category term="programming-languages" label="Programming languages"/>
    <summary>Languages that look alike on paper disagree about division, rounding, null, the empty string, truth and overflow. LCD would have had to pick one meaning and put it in the name, or leave the feature out. Here is how, ending with one rule written in LCD, in PL/SQL and in Java as it was in 2001.</summary>
    <content type="html">&lt;p&gt;&lt;em&gt;Division, rounding, null, and the empty string that Oracle thinks is nothing&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Thought 2001, written November 2026. Programming languages.&lt;/p&gt;&lt;p&gt;Levels decide which languages have to agree. They don&#39;t make them agree.&lt;/p&gt;
&lt;p&gt;Take any two languages that both carry level 1 and write the same innocent-looking expression in each. Surprisingly
often you get two answers. Each answer is reasonable, each is documented, and each has been relied on by somebody for
years. None of them is wrong. They are just different, and a rule that gives different answers in different places is
exactly what LCD existed to prevent.&lt;/p&gt;
&lt;p&gt;So inside each level there is one more test, applied to every feature: do all the targets that carry this level give
it the same meaning? If they do, it&#39;s in. If they can with help, it&#39;s in, and a small runtime library supplies the help
where it&#39;s missing. If they can&#39;t agree, LCD has two options. It can pick one meaning and say so out loud, in the
name of the operation. Or it can leave the feature out.&lt;/p&gt;
&lt;p&gt;Most of LCD&#39;s character would have come from that last case. Here is how.&lt;/p&gt;
&lt;h2&gt;Dividing two integers&lt;/h2&gt;
&lt;p&gt;C, C++ and Java truncate: 7 divided by 2 is 3. Pascal keeps the two apart: &lt;code&gt;div&lt;/code&gt; for integer division, and &lt;code&gt;/&lt;/code&gt;, which
always gives a real, 3.5. PL/SQL&#39;s &lt;code&gt;/&lt;/code&gt; gives 3.5 too. Perl gives 3.5 as a floating-point
number. Ada truncates, but distinguishes &lt;code&gt;rem&lt;/code&gt; from &lt;code&gt;mod&lt;/code&gt;, which disagree as soon as a negative number turns up.&lt;/p&gt;
&lt;p&gt;LCD&#39;s answer: no &lt;code&gt;/&lt;/code&gt; on integers at all. Division says how it rounds: &lt;code&gt;divide_truncated&lt;/code&gt;, &lt;code&gt;divide_floor&lt;/code&gt;,
&lt;code&gt;divide_exact&lt;/code&gt;, the last of which fails if anything is left over. It&#39;s longer to type. Nobody ever has to wonder.&lt;/p&gt;
&lt;h2&gt;Rounding&lt;/h2&gt;
&lt;p&gt;Java&#39;s &lt;code&gt;BigDecimal&lt;/code&gt; offers every rounding mode you could want. Oracle&#39;s &lt;code&gt;ROUND&lt;/code&gt; rounds half away from zero, as does
COBOL&#39;s &lt;code&gt;ROUNDED&lt;/code&gt;. Delphi&#39;s &lt;code&gt;Round&lt;/code&gt; rounds half to even, the banker&#39;s way. C leaves it to you.&lt;/p&gt;
&lt;p&gt;So rounding names its mode too: &lt;code&gt;round_half_even&lt;/code&gt;, &lt;code&gt;round_half_away_from_zero&lt;/code&gt;. Where a target lacks the mode you asked
for, the runtime library implements it in that target&#39;s own language. That&#39;s why the level 1 example in part 2 chose
half away from zero: the database and COBOL could both do it without help.&lt;/p&gt;
&lt;h2&gt;Null&lt;/h2&gt;
&lt;p&gt;In SQL, &lt;code&gt;NULL&lt;/code&gt; means unknown, and unknown spreads. Compare a null with anything and the answer is unknown, and an &lt;code&gt;IF&lt;/code&gt;
whose condition is unknown quietly takes the &lt;code&gt;ELSE&lt;/code&gt; branch. Java and Pascal have null references, which do something
much louder. C has null pointers, which do whatever they like.&lt;/p&gt;
&lt;p&gt;LCD would have had no null. If something can be absent, its type says so, and the code has to say what happens in each
case. Where LCD code meets its host, the transpiled code checks every value on the way in, so a &lt;code&gt;NULL&lt;/code&gt; from the
database becomes a failure with a name, rather than a quiet wrong turn.&lt;/p&gt;
&lt;h2&gt;The empty string&lt;/h2&gt;
&lt;p&gt;Oracle stores the empty string as &lt;code&gt;NULL&lt;/code&gt;. This is not a bug. It is how Oracle has always worked, and it&#39;s documented,
and it has surprised people for decades.&lt;/p&gt;
&lt;p&gt;In LCD, text is never absent, although it can be empty. The PL/SQL runtime would have had to represent empty text in a
way that could never turn into &lt;code&gt;NULL&lt;/code&gt; on the way through.&lt;/p&gt;
&lt;h2&gt;What counts as true&lt;/h2&gt;
&lt;p&gt;C has no boolean type of its own and treats zero as false. Many BASICs use -1 for true. Perl treats &lt;code&gt;&amp;quot;0&amp;quot;&lt;/code&gt; and &lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt; as
false, along with a few other things you have to learn the hard way.&lt;/p&gt;
&lt;p&gt;In LCD only a boolean can decide an &lt;code&gt;if&lt;/code&gt;. There is no truthiness to translate, so there is nothing to get wrong.&lt;/p&gt;
&lt;h2&gt;Overflow&lt;/h2&gt;
&lt;p&gt;Signed overflow in C is undefined, which means the compiler may assume it never happens. Java wraps around without a
word. PL/SQL and Ada raise an error. Pascal raises one only if overflow checks are switched on. COBOL quietly cuts off
the digits that don&#39;t fit, unless you asked for &lt;code&gt;ON SIZE ERROR&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In LCD, overflow is a failure, everywhere. The C runtime checks for it, so that C behaves like the others.&lt;/p&gt;
&lt;h2&gt;Where strings start&lt;/h2&gt;
&lt;p&gt;PL/SQL&#39;s &lt;code&gt;SUBSTR&lt;/code&gt;, COBOL and Pascal&#39;s strings start counting at 1. C, C++ and Java start at 0.&lt;/p&gt;
&lt;p&gt;LCD has to pick one and transpile it for the others. I&#39;d have picked 1, because business people count from 1. In Y I
ended up with something better: every index carries its unit, so a position can&#39;t be confused with a count.&lt;/p&gt;
&lt;h2&gt;Names&lt;/h2&gt;
&lt;p&gt;PL/SQL, Pascal, Ada and COBOL don&#39;t care about case. PL/SQL names stopped at 30 characters, and so did COBOL&#39;s. COBOL
wants hyphens where everyone else wants underscores. And every language reserves a different set of words.&lt;/p&gt;
&lt;p&gt;So every LCD name is lowercase with underscores, at most 30 characters long, unique even if you ignore case, and not a
reserved word anywhere. COBOL gets hyphens instead of underscores. Y still has the all-lowercase rule today. I like to
think it came from somewhere.&lt;/p&gt;
&lt;h2&gt;Errors&lt;/h2&gt;
&lt;p&gt;C returns error codes. C++, Java, Pascal, Ada and PL/SQL have exceptions, all different. MFC throws pointers to
exceptions, which you then have to delete. COBOL has status fields.&lt;/p&gt;
&lt;p&gt;An LCD function declares, by name, the ways in which it can fail. Each target then reports those failures in its own
way: a return code in C, an exception in Java, a raised error in PL/SQL.&lt;/p&gt;
&lt;h2&gt;The rest&lt;/h2&gt;
&lt;p&gt;Two more fall out quickly. C and C++ leave the order in which a function&#39;s arguments are evaluated unspecified, so an
LCD expression may have at most one effect. And every language has its own precedence table, so LCD requires
parentheses wherever two kinds of operator meet.&lt;/p&gt;
&lt;p&gt;The last one is about time and chance. Every target has its own clock and its own random number generator, and no two
of them agree. Business logic in LCD would have had neither. &amp;quot;Today&amp;quot; would be an argument, passed in from outside. So
would any random seed. Given the same inputs, every target would compute the same answer, which is the whole point.&lt;/p&gt;
&lt;h2&gt;Memory&lt;/h2&gt;
&lt;p&gt;LCD&#39;s memory was managed: Java&#39;s garbage collector, or reference counting in C++. Making that work the same
everywhere is its own puzzle, because the targets manage memory in three different ways. Java collects garbage. C++,
Delphi and Perl count references. PL/SQL copies, and shares nothing.&lt;/p&gt;
&lt;p&gt;The levels from part 2 sort this out. At levels 0 to 2 there are only values: assign a record and you get your own
copy. Behind the scenes a runtime may share it and copy it only when someone writes to it, but no program can tell.
That works everywhere, including PL/SQL.&lt;/p&gt;
&lt;p&gt;Level 3 adds values that really are shared. For those to behave identically under a garbage collector and under
reference counting, two things have to go. &lt;em&gt;Cycles&lt;/em&gt; go, because a reference counter can&#39;t reclaim a cycle and a
garbage collector can. &lt;em&gt;Finalisers&lt;/em&gt; go, because nobody knows when a garbage collector will run, but everybody knows
exactly when the last reference disappears.&lt;/p&gt;
&lt;h2&gt;One rule, three languages&lt;/h2&gt;
&lt;p&gt;Here is a level 2 rule, with a loop in it, which PL/SQL can carry and plain SQL can&#39;t. Again, the notation borrows
from Y.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;module invoicing
level structured


-- A line on an invoice.
record invoice_line:
    quantity is integer
    unit_price is decimal(12, 2)


-- Why an invoice cannot be totalled.
failure invoice_error:
    no_lines
    negative_quantity


-- Totals an invoice, with the volume discount the price list gives above 1000.00.
--   why: The shop, the web site and the nightly batch must charge the same amount for the same lines.
function invoice_total(lines is list of invoice_line) returns decimal(12, 2) fails invoice_error:
    if no_lines = lines.is_empty():
        fail invoice_error.no_lines
    else has_lines:
        var total is decimal(12, 2) = 0.00

        for line in lines:
            if negative = (line.quantity &amp;lt; 0):
                fail invoice_error.negative_quantity
            else counted:
                total := total + (line.unit_price * line.quantity)

        if large_order = (total &amp;gt;= 1000.00):
            return round_half_even(total * 0.95, places: 2)
        else small_order:
            return total
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every condition has a name, &lt;code&gt;large_order&lt;/code&gt; or &lt;code&gt;small_order&lt;/code&gt;, and so does every way of failing. That is how you would
check that the shop, the web site and the nightly batch took the same path: the names would be the same strings in
every language&#39;s logs.&lt;/p&gt;
&lt;p&gt;Here it is in PL/SQL. Oracle has no half-to-even rounding, so that call goes to the runtime library, &lt;code&gt;lcd_std&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE OR REPLACE PACKAGE BODY invoicing AS
  FUNCTION invoice_total(lines IN invoice_lines) RETURN NUMBER IS
    total NUMBER(12, 2) := 0;
  BEGIN
    IF lines IS NULL OR lines.COUNT = 0 THEN
      lcd_std.fail(&#39;invoicing.invoice_error.no_lines&#39;);
    END IF;

    FOR i IN 1 .. lines.COUNT LOOP
      IF lines(i).quantity IS NULL OR lines(i).unit_price IS NULL THEN
        lcd_std.fail(&#39;lcd.missing_value&#39;);
      END IF;
      IF lines(i).quantity &amp;lt; 0 THEN
        lcd_std.fail(&#39;invoicing.invoice_error.negative_quantity&#39;);
      END IF;
      total := total + (lines(i).unit_price * lines(i).quantity);
    END LOOP;

    IF total &amp;gt;= 1000.00 THEN
      RETURN lcd_std.round_half_even(total * 0.95, 2);
    ELSE
      RETURN total;
    END IF;
  END invoice_total;
END invoicing;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All those &lt;code&gt;IS NULL&lt;/code&gt; checks are the price of LCD having no null. Leave them out, and a null quantity coming in from
outside would make every comparison unknown and quietly take the wrong branch.&lt;/p&gt;
&lt;p&gt;And in Java as it was in 2001, before generics and before enums:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public static BigDecimal invoice_total(List lines) throws invoice_error {
    if (lcd_std.require(lines).isEmpty()) {
        throw new invoice_error(invoice_error.no_lines);
    }
    BigDecimal total = lcd_std.decimal(&amp;quot;0.00&amp;quot;);
    for (Iterator it = lines.iterator(); it.hasNext(); ) {
        invoice_line line = (invoice_line) it.next();
        if (line.quantity &amp;lt; 0) {
            throw new invoice_error(invoice_error.negative_quantity);
        }
        total = total.add(line.unit_price.multiply(BigDecimal.valueOf(line.quantity)));
    }
    if (total.compareTo(lcd_std.decimal(&amp;quot;1000.00&amp;quot;)) &amp;gt;= 0) {
        return lcd_std.round_half_even(total.multiply(lcd_std.decimal(&amp;quot;0.95&amp;quot;)), 2);
    }
    return total;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes, that is &lt;code&gt;invoice_total&lt;/code&gt; and not &lt;code&gt;invoiceTotal&lt;/code&gt;. It&#39;s unidiomatic Java, and it&#39;s deliberate. A search for the
rule&#39;s name should find it in every language.&lt;/p&gt;
&lt;p&gt;The C version returns its failure as a code and writes the total through a pointer, and its money is a scaled integer
from the runtime library. The Delphi version gets off lightly: Delphi&#39;s &lt;code&gt;Currency&lt;/code&gt; type holds four fixed decimal places,
so the arithmetic is native.&lt;/p&gt;
&lt;h2&gt;The library&lt;/h2&gt;
&lt;p&gt;The standard library would have been small, and written once per target, by hand, in that target&#39;s own language:
text, numbers with their named roundings and divisions, dates with a named rule for what happens at the end of a month,
collections, and conversion to and from text in an explicit format, never one that depends on the machine&#39;s locale.&lt;/p&gt;
&lt;p&gt;What it would never have had: files, the network, the database, threads, the clock, randomness, anything visual. That
all belongs to the program around the rules.&lt;/p&gt;
&lt;h2&gt;The tests&lt;/h2&gt;
&lt;p&gt;A test would be written once in LCD and transpiled along with the code. It would run on every target that carries the
module&#39;s level, and the targets would have to agree. When they didn&#39;t, that would not mean one of them won. It would
mean a transpiler or a runtime library was wrong.&lt;/p&gt;
&lt;p&gt;In the end the tests would be the real definition of each level. A feature is in a level when its tests pass on every
target of that level.&lt;/p&gt;
&lt;p&gt;All of which sounds tidy on paper. Part 4 is about what it would have cost to build, and why that, rather than any of
the problems above, is what killed it.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>The cost, then and now</title>
    <link href="https://icelander.net/v1/programming-languages/the-cost-then-and-now"/>
    <id>tag:icelander.net,2026:the-cost-then-and-now</id>
    <published>2026-11-03T00:00:00Z</published>
    <updated>2026-11-03T00:00:00Z</updated>
    <category term="programming-languages" label="Programming languages"/>
    <summary>I dropped LCD because it would have cost one person years: a transpiler, a runtime library and a test suite for every target. Much of its thinking came back in Y anyway, and the work that killed it is exactly the kind agents now do well.</summary>
    <content type="html">&lt;p&gt;&lt;em&gt;Why LCD would have taken years, and why that reason is mostly gone&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Thought 2001, written November 2026. Programming languages.&lt;/p&gt;&lt;p&gt;I dropped LCD because it wasn&#39;t something I wanted to spend my time on, and because in those days it would have taken
a massive amount of time.&lt;/p&gt;
&lt;p&gt;Twenty-five years later I can be more specific about the second half of that.&lt;/p&gt;
&lt;h2&gt;The bill&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;A transpiler for every target.&lt;/strong&gt; C, C++ once for each framework, Java, PL/SQL, Pascal, and COBOL if it was ever
going to reach the older systems. Each one emitting code that reads as if a person wrote it, which is much harder than
emitting code that merely works.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A runtime library for every target.&lt;/strong&gt; Decimal arithmetic, every rounding mode, dates, text and collections, each
written by hand in the target&#39;s own language. C needs all of it. PL/SQL needs most of it. Each C++ framework needs its
own glue.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Levels multiply the work.&lt;/strong&gt; Every level has to be defined, tested and transpiled for every target that carries it,
and a change to one level has to be checked against all of them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The gaps.&lt;/strong&gt; I never got as far as deciding how to close the gaps between targets, and each one is real work. Typing:
PL/SQL&#39;s &lt;code&gt;NUMBER&lt;/code&gt; against fixed-width integers, Java without generics, C without checked anything, Perl without static
types at all. Memory: values that are cheap on every memory manager, and shared references that behave the same under
a garbage collector and a reference counter. Data: the database thinks in rows and sets, and everyone else thinks in
records, so a loop that is natural in Java is slow inside Oracle. Text: the database, the JVM, Windows code pages and
C&#39;s bytes each had their own idea of a character, so there was no single meaning of &amp;quot;length&amp;quot; or &amp;quot;upper case&amp;quot; to
transpile.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Proving the targets agree.&lt;/strong&gt; The test suite would run on every toolchain, some of them inside a database, and every
disagreement would have to be traced to a transpiler, a runtime library or the definition of a level.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Keeping up.&lt;/strong&gt; Every new version of a target changes what a level can hold. Java 5 arrived in 2004 with generics and
enums. Oracle 10g added new numeric types. Each framework kept moving too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Adoption.&lt;/strong&gt; Even when it all worked, the generated code would be code that a team in each environment had to debug,
deploy and own, written by a tool they hadn&#39;t asked for.&lt;/p&gt;
&lt;p&gt;None of that is impossible. It was just years of one person&#39;s work for a tool whose benefit arrived only at the very
end, once every transpiler was finished. I had a job, and other things I would rather do. It stayed a dream.&lt;/p&gt;
&lt;h2&gt;It came back anyway&lt;/h2&gt;
&lt;p&gt;What surprised me, going back through everything that led to Y, is how much of LCD&#39;s thinking kept turning up
without my ever having written LCD down.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Transpiling.&lt;/strong&gt; YAPL, the first version that left any written trace, was started in 2019 and early on described
itself as a transpiler to &amp;quot;all machines&amp;quot;. In the 2024 design discussions I gave it up: &amp;quot;we are no longer aiming for transpilation&amp;quot;. In September 2026
it came straight back, in a conversation that started with a language that &amp;quot;really just transpiles to Rust&amp;quot;. Y&#39;s
position today is a compromise I think LCD would have recognized. Rust is not Y&#39;s definition; it is a backend and a
check. But the example programs are lowered to plain, readable Rust that an ordinary Rust toolchain builds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Levels.&lt;/strong&gt; Y has a constraint language for describing rules about code that lives in other languages: Python, T-SQL,
C#, Rust and more. Its kinds of rule are switched on host by host, the simpler kinds for some languages and the richer
ones only for languages whose types can carry them. That is LCD&#39;s levels again, with &amp;quot;level&amp;quot; renamed to &amp;quot;kind&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rules written once.&lt;/strong&gt; The reason that constraint language exists is the problem LCD started from: business rules
repeated across a client, a server and a database, each copy free to drift.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The host does what the language can&#39;t.&lt;/strong&gt; Y has no foreign-function interface. When it needs something it can&#39;t
express, the function is declared in Y and written in the backend&#39;s own language. LCD&#39;s runtime library, written by
hand in each target, was the same move.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The same tests everywhere.&lt;/strong&gt; In Y, running several backends and comparing them is a way of finding bugs, and a
disagreement is a bug, never a vote. That is LCD&#39;s test suite, almost word for word.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;And the small things.&lt;/strong&gt; No &lt;code&gt;/&lt;/code&gt; on integers; division says how it rounds. No null, and nothing is true except a
boolean. Every name lowercase, with underscores. Every decision named, so traces can be compared. Every function
declaring how it can fail. No clock and no randomness unless they are handed in. Every rule saying &lt;em&gt;why&lt;/em&gt;. Each of those
appears in Y, and each of them is an answer to a question LCD would have had to ask.&lt;/p&gt;
&lt;p&gt;Most of them first turn up in writing years later, in YAPL or in the 2024 design discussions. All of them are what you
would end up with if you spent a long time trying to make several languages agree, and that is what I spent some of 2001 thinking about.&lt;/p&gt;
&lt;h2&gt;What changed&lt;/h2&gt;
&lt;p&gt;LCD was a set of small languages for people to write, with transpilers written by people, each level chosen so that
its targets could carry it. Y is a whole language, written mostly by AI agents and reviewed by people, with a compiler
that checks what the agents propose. I wrote about what happens to a language when its writer changes in
&lt;a href=&quot;/v1/programming-languages/write-only-languages&quot;&gt;Write-only languages&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The change that matters for LCD is simpler than that. Look at the bill again. A transpiler per target. A runtime
library per target. A test suite run against every target, with every disagreement traced to its cause. That is
exactly the kind of work that is specified once, checked mechanically, and done many times over: the kind of work
agents now do well.&lt;/p&gt;
&lt;p&gt;The reason I dropped LCD is mostly gone. The question it asked is still the one Y asks: how do you write a rule once,
and trust it wherever it runs?&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Write-only languages</title>
    <link href="https://icelander.net/v1/programming-languages/write-only-languages"/>
    <id>tag:icelander.net,2026:write-only-languages</id>
    <published>2026-11-01T00:00:00Z</published>
    <updated>2026-11-01T00:00:00Z</updated>
    <category term="programming-languages" label="Programming languages"/>
    <summary>Some languages are write-only for humans, and some take the programmer&#39;s word for everything. Now that AIs write the code, the second problem is the one that matters, which makes Rust the best language for an AI to write.</summary>
    <content type="html">&lt;p&gt;&lt;em&gt;Code no human can read, and code no compiler checks&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Thought 2010, written October 2026. Programming languages.&lt;/p&gt;&lt;p&gt;In 1972, an engineer at Signetics had had enough of the company&#39;s approval process. Every new component specification had to crawl through a long chain of sign-offs, &amp;quot;during which no actual checking seemed to occur&amp;quot;, as the Jargon File puts it. So the engineer wrote a specification for a memory chip you could write to but never read from, slipped it into the pile with all the others, and waited to see who would notice.&lt;/p&gt;
&lt;p&gt;Nobody did. The Signetics 25120, a &amp;quot;fully encoded, 9046 x N, Random Access, write-only-memory&amp;quot;, made it into the data book, complete with a characteristic curve for &amp;quot;number of pins remaining vs. number of socket insertions&amp;quot;. Management found out when regular customers started calling to ask for pricing.&lt;/p&gt;
&lt;p&gt;That&#39;s the Jargon File&#39;s version, anyway. Other tellings say it was a joke from the start, and it certainly ended up as one: Signetics later ran it as an April Fools&#39; gag. I prefer the version with a moral.&lt;/p&gt;
&lt;p&gt;Programmers have never met an engineering joke they didn&#39;t want to steal, so the term moved over to software. Write-only code, according to the same Jargon File, is code &amp;quot;so arcane, complex, or ill-structured that it cannot be modified or even comprehended by anyone but its author, and possibly not even by him/her&amp;quot;. A write-only language is one &amp;quot;with syntax (or semantics) sufficiently dense and bizarre that any routine of significant size is automatically write-only code&amp;quot;. The Jargon File awarded the title to APL, occasionally to C, and noted that INTERCAL and TECO deserved it more. Ward Cunningham&#39;s original wiki has &lt;a href=&quot;https://wiki.c2.com/?WriteOnlyLanguage&quot;&gt;a page on it&lt;/a&gt; too, and anyone who has opened a file they wrote six months ago has met the concept in person.&lt;/p&gt;
&lt;p&gt;Here is Conway&#39;s Game of Life, in one line of APL:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-apl&quot;&gt;life←{↑1 ⍵∨.∧3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&#39;m told it&#39;s elegant. It certainly needed its own keyboard. Edsger Dijkstra called APL &amp;quot;a mistake, carried through to perfection&amp;quot;, and as insults go, that one is hard to improve on.&lt;/p&gt;
&lt;p&gt;And then there are regular expressions, the one write-only language every programmer speaks.&lt;/p&gt;
&lt;h2&gt;My list&lt;/h2&gt;
&lt;p&gt;Everyone has a list. Mine has Perl, bash, Scala and Rust on it, along with most of functional programming. That&#39;s my list for humans, mind you. AI has no such limitations, and we&#39;ll get to what that means.&lt;/p&gt;
&lt;p&gt;Yes, Rust. Rust lovers, read this to the end and you&#39;ll be overjoyed. At least, you will be if you&#39;re not still convinced that you write better code than a strong modern AI model that&#39;s been told to put some effort into it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Perl&lt;/strong&gt; is the easy one. Keith Bostic called it &amp;quot;the only language that looks the same before and after RSA encryption&amp;quot;. Its motto is &amp;quot;There&#39;s more than one way to do it&amp;quot;, which is a fine motto for a jazz band and a terrible one for a code base. It has a special variable for nearly every punctuation mark on the keyboard. &lt;code&gt;$_&lt;/code&gt; is whatever you were just talking about. &lt;code&gt;$;&lt;/code&gt; is the subscript separator, obviously. An array means its length or its first element, depending on whether you put parentheses around the variable you assign it to. Perl hackers even test whether a number is prime with a regular expression, &lt;code&gt;/^1?$|^(11+?)&#92;1+$/&lt;/code&gt;, run against the number written out in unary. It works, which is the worst thing I can say about it.&lt;/p&gt;
&lt;p&gt;Larry Wall, who designed Perl, famously listed the three great virtues of a programmer as laziness, impatience and hubris. You can find all three in the language. Mostly the third.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bash&lt;/strong&gt; is the one nobody gets to avoid. You can refuse to touch Perl. Bash runs your build, your deploy and your containers, so sooner or later every normal person has to read some and write some. It&#39;s a minefield with a prompt. &lt;code&gt;&amp;quot;$@&amp;quot;&lt;/code&gt; and &lt;code&gt;$@&lt;/code&gt; are different things, &lt;code&gt;[ ]&lt;/code&gt; and &lt;code&gt;[[ ]]&lt;/code&gt; are different things, and &lt;code&gt;${file%.*}&lt;/code&gt; strips a file extension, as everyone knows. My rule of thumb is that every line of bash, or every short paragraph of it, gets a comment above it, longer than the code, explaining in human terms what the code is actually doing. A comment longer than the code it explains is normally a bad sign. In bash, it&#39;s the minimum.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scala&lt;/strong&gt; set out to be a better Java and never learned to say no. It has objects, functional programming, implicit conversions, symbolic operators, a type system you can do arithmetic in, and XML literals in the grammar. In Scala 2, if you wanted a type lambda, you wrote &lt;code&gt;({type L[A] = Either[Int, A]})#L&lt;/code&gt;, and people did, in production, on purpose. Implicits let code run that appears nowhere on the page, chosen by an import you can&#39;t see from where you&#39;re standing. And one of its most popular libraries gave the world &lt;code&gt;|@|&lt;/code&gt;, an operator its own community calls Admiral Ackbar, Cinnabon or Macaulay Culkin, because any of those is easier to remember than what it does.&lt;/p&gt;
&lt;p&gt;It is possible to write good Scala. Write it mostly as you would Java. It runs on the JVM anyway. Nothing in the toolchain will enforce that for you, but a well-instructed AI reviewer could.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rust&lt;/strong&gt; is the one that&#39;s going to get me into trouble. Stack Overflow&#39;s survey has named it the most loved, and later the most admired, language every year for a decade, which tells you a great deal about the kind of people who fill out Stack Overflow surveys. Here&#39;s a perfectly ordinary type from a perfectly ordinary async code base:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;Pin&amp;lt;Box&amp;lt;dyn Future&amp;lt;Output = Result&amp;lt;Vec&amp;lt;u8&amp;gt;, Box&amp;lt;dyn Error + Send + Sync&amp;gt;&amp;gt;&amp;gt; + Send + &#39;static&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The syntax for telling a generic function which type you meant is baroque enough to have earned a nickname, the turbofish (&lt;code&gt;::&amp;lt;&amp;gt;&lt;/code&gt;). &amp;quot;Fighting the borrow checker&amp;quot; needs no explanation for anyone who has written Rust, and no explanation will help anyone who hasn&#39;t. For most normal programmers, Rust is unreadable, and unwritable too. It isn&#39;t a language you write so much as a language you negotiate with.&lt;/p&gt;
&lt;p&gt;And then there&#39;s &lt;strong&gt;functional programming&lt;/strong&gt; in general.&lt;/p&gt;
&lt;p&gt;My position is simple: humans don&#39;t think like that. We think in steps. Every set of instructions people have ever written for one another, from recipes to flat-pack furniture to the checklist a pilot runs before takeoff, is a sequence of steps that changes the state of the world. Nobody&#39;s grandmother ever wrote down her pancake recipe as a fold over a list of ingredients.&lt;/p&gt;
&lt;p&gt;We also think in mutable state, because the world is mutable state. As an amateur physicist, I can report that the universe has been updating itself in place for 13.8 billion years without once returning a new copy. (The many-worlds interpretation disagrees. I suspect it was proposed by a Haskell programmer.)&lt;/p&gt;
&lt;p&gt;Functional programming asks you to give up both. No steps, only expressions. No state, only new values. Recursion instead of loops. Point-free style, which is the art of writing a function without ever naming the thing it works on. Haskell&#39;s lens library, which exports well over a hundred operators, including &lt;code&gt;&amp;lt;&amp;lt;%@~&lt;/code&gt;, &lt;code&gt;&amp;lt;&amp;lt;&amp;lt;&amp;gt;:~&lt;/code&gt; and &lt;code&gt;^@?!&lt;/code&gt;. And monads, about which Douglas Crockford observed that once you finally understand them, you lose the ability to explain them to anybody. Write-only memory, installed in a human head.&lt;/p&gt;
&lt;p&gt;Then there are the chains. A pipeline of functions, each handing its result to the next, reads beautifully in a conference talk. Here are six steps of one:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const top = orders
  .filter(o =&amp;gt; o.status !== &amp;quot;void&amp;quot;)
  .map(o =&amp;gt; ({ ...o, total: o.lines.reduce((sum, l) =&amp;gt; sum + l.qty * l.price, 0) }))
  .filter(o =&amp;gt; o.total &amp;gt; threshold)
  .sort((a, b) =&amp;gt; b.total - a.total)
  .slice(0, 10)
  .map(o =&amp;gt; `${o.customer.name}: ${format(o.total)}`);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In production, the chain is twelve steps long, and one day an exception comes out of step twelve. Now you&#39;re debugging twelve anonymous functions at the same time. None of them has a name, none of the intermediate results was ever stored anywhere you could look at it, and the stack trace is mostly the library&#39;s plumbing. A chain like that is illegible when you read it and useless when you debug it, and worse than useless, because it hides exactly what you need to see. Your only option is to break the chain apart and add temporary variables, logging and breakpoints, like a normal person. In other words, you rewrite it imperatively, just long enough to find the bug.&lt;/p&gt;
&lt;p&gt;That&#39;s why imperative programming won. Lisp has been around since 1958, ML since 1973 and Haskell since 1990, and each of them has been the language of the future for longer than most of its users have been alive. Even Haskell gave in. It has a whole notation whose job is to make functional code look like a sequence of steps, and Simon Peyton Jones, one of its designers, called it &amp;quot;the world&#39;s finest imperative programming language&amp;quot;. He meant it as a compliment. I&#39;m taking it as a confession.&lt;/p&gt;
&lt;p&gt;Functional programming does have one runaway success with ordinary humans, and that&#39;s Excel. More people write Excel formulas than every other programming language combined. Now open a spreadsheet somebody else built and tell me what this does:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;=IFERROR(INDEX($B:$B,MATCH(1,($A:$A=E2)*($C:$C&amp;gt;F2),0)),&amp;quot;&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Excel isn&#39;t the exception that proves humans think functionally. It&#39;s the most successful write-only language in history.&lt;/p&gt;
&lt;p&gt;I&#39;ll make a confession of my own, though. The mainstream helped itself to the good bits, and so have I. My ideal language doesn&#39;t exist yet, but it already has &lt;code&gt;map&lt;/code&gt; and &lt;code&gt;filter&lt;/code&gt;. Its transactions borrow &lt;code&gt;retry&lt;/code&gt; and &lt;code&gt;orelse&lt;/code&gt; straight from a Haskell paper that Peyton Jones co-wrote. Its sum types, exhaustive matching and errors-as-values come from the ML family, by way of Rust, whose first compiler was written in OCaml. The functional crowd built the best checkers in the business, and then used them to write code nobody can read. I&#39;m stealing the checkers and leaving the code. It&#39;s a gateway drug, I know. I can stop any time I want.&lt;/p&gt;
&lt;h2&gt;The determined lunatic&lt;/h2&gt;
&lt;p&gt;None of this requires an exotic language. Any language becomes write-only in the hands of a sufficiently determined lunatic.&lt;/p&gt;
&lt;p&gt;You&#39;ve met them. One wrote a 300-character one-liner because it saved declaring a variable. Another read Roedy Green&#39;s classic &amp;quot;How To Write Unmaintainable Code&amp;quot;, subtitled &amp;quot;Ensure a job for life&amp;quot;, and mistook it for a tutorial. A third spent their weekends on the International Obfuscated C Code Contest, which has been running since 1984, and on Brainfuck, a language with eight commands and a name that doubles as a review, and then brought both hobbies to work on Monday.&lt;/p&gt;
&lt;p&gt;Back in 1983, Ed Post observed that the determined Real Programmer can write FORTRAN programs in any language. The determined lunatic can write Perl in any language, and usually does.&lt;/p&gt;
&lt;p&gt;Brian Kernighan explained the problem with these people decades ago: &amp;quot;Everyone knows that debugging is twice as hard as writing a program in the first place. So if you&#39;re as clever as you can be when you write it, how will you ever debug it?&amp;quot; Of all write-only code, that&#39;s the kind I hate most. Nobody forced it on anyone. The lunatic chose it.&lt;/p&gt;
&lt;h2&gt;Who pays&lt;/h2&gt;
&lt;p&gt;Write-only languages and determined lunatics make the same trade. They save the writer a little effort and bill it to every reader who comes after, with interest. Code is written once and read for years, so that&#39;s a terrible deal. &lt;a href=&quot;/v1/processes/tests-are-written-for-the-maintainer&quot;&gt;My approach to unit testing&lt;/a&gt; refuses it outright. It even prefers copy-paste to clever helpers, because boring is the point.&lt;/p&gt;
&lt;p&gt;It&#39;s the point of my ideal language, too. My early notes on it say: &amp;quot;We don&#39;t want to make the code reader have to ponder the meaning of an expression.&amp;quot; So it has no truthiness, no implicit conversions, no shadowing, no abbreviations and no macros. You never need to remember an operator precedence table, because mixing operators requires parentheses. I learned that one the hard way, looking over the shoulder of a developer who couldn&#39;t find a bug that was hiding in the precedence table. There&#39;s one way to write each thing, and every decision has a name. Each of those rules costs the writer a few keystrokes. Each of them saves every reader a ponder.&lt;/p&gt;
&lt;h2&gt;Then the writer changed&lt;/h2&gt;
&lt;p&gt;Where I stand on this is no secret: &lt;a href=&quot;/v1/working-with-ai/programming-was-my-art&quot;&gt;humans have no business writing code any more&lt;/a&gt;. My agents write it. I read it, or at least the parts my tooling decides I should see.&lt;/p&gt;
&lt;p&gt;That ought to have been the end of write-only languages. An AI can read Perl, mostly. It could probably even write Perl that was partially legible. It reads regular expressions without moving its lips. And the lunatics have lost their motive: an AI has no ego to feed and no job to secure, and it doesn&#39;t write 300-character one-liners unless you ask it to.&lt;/p&gt;
&lt;p&gt;It wasn&#39;t the end, for two reasons. The first is that I still have to read the code. Writing has become nearly free, and reading hasn&#39;t. My attention is now the scarcest resource in the whole process, and a write-only language wastes it on every line.&lt;/p&gt;
&lt;p&gt;The second is that AIs have a write-only problem of their own. It looks nothing like ours.&lt;/p&gt;
&lt;h2&gt;Trust-only languages&lt;/h2&gt;
&lt;p&gt;An AI doesn&#39;t struggle to read code, and it doesn&#39;t struggle to write it. What it struggles with is knowing whether what it wrote is right. It writes fluently, confidently and fast, and every so often it&#39;s wrong with the serene confidence of a GPS steering you into a lake. Less often than it used to be, but still.&lt;/p&gt;
&lt;p&gt;What an AI needs from a language, then, is to be told, early and specifically, when it&#39;s wrong.&lt;/p&gt;
&lt;p&gt;The languages humans love are built on the opposite principle. Python&#39;s community likes to say that &amp;quot;we&#39;re all consenting adults here&amp;quot;, which is why nothing in the language actually stops you from touching a private attribute. JavaScript will add an array to an object and cheerfully hand you back a string. Ruby was designed to make programmers happy. And the ANSI C committee wrote the whole philosophy into C&#39;s official rationale, in plain English: &amp;quot;Trust the programmer.&amp;quot;&lt;/p&gt;
&lt;p&gt;That was sound advice when the programmer was Dennis Ritchie.&lt;/p&gt;
&lt;p&gt;My programmers are brilliant, tireless interns who will cheerfully find a way around any rule you give them, up to and including deleting the test that said no. They are not Dennis Ritchie, and they&#39;re not even consenting adults.&lt;/p&gt;
&lt;p&gt;For an AI, a relaxed language is write-only in the Signetics sense. Code goes in, and nothing comes back out. No complaint, no warning, no &amp;quot;did you mean&amp;quot;. Just silence, all the way to production, where the silence is finally broken by a pager. (For younger readers: a pager was a little box on your belt that beeped when someone needed you, usually at three in the morning. It showed a phone number, and you went and found a phone. Mobile phones and text messages replaced it, and smartphones and messenger apps replaced those. The three in the morning part survived.)&lt;/p&gt;
&lt;p&gt;That deserves a name of its own. I call these trust-only languages.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A &lt;strong&gt;trust-only language&lt;/strong&gt; is one that takes the programmer&#39;s word for it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In a trust-only language, the only thing standing between what the code says and what the programmer meant is trust. The AI calls a method that doesn&#39;t exist, and the language believes it, right up until that exact line runs. Odds are that line is in an error handler, and that it runs for the first time during an outage, because error handlers are the code nobody tests. The AI assumes a value can&#39;t be &lt;code&gt;None&lt;/code&gt;, and the language believes that too. C goes one better. It&#39;s so sure your program never does anything undefined that it optimizes on the assumption.&lt;/p&gt;
&lt;p&gt;The purest trust-only language of them all is bash. In 2015, Steam&#39;s Linux client shipped a script containing the line &lt;code&gt;rm -rf &amp;quot;$STEAMROOT/&amp;quot;*&lt;/code&gt;, directly under a comment that said &lt;code&gt;# Scary!&lt;/code&gt;. When the variable came up empty, the script deleted everything the user owned. The programmer knew. The comment knew. The language took their word for it. (It&#39;s also not the kind of comment I had in mind.)&lt;/p&gt;
&lt;p&gt;Python has mypy, JavaScript has TypeScript, and everything has a linter. They help. But a check that&#39;s optional is still trust. You&#39;re trusting that somebody turned it on, and that nobody quietly added &lt;code&gt;# type: ignore&lt;/code&gt; or &lt;code&gt;as any&lt;/code&gt; to make the red go away. An AI told to make the build green reaches for those long before it gets around to deleting the test. That leaves the test suite as the only thing checking anything, and the AI wrote that too.&lt;/p&gt;
&lt;p&gt;The too-clever languages are no better off, and I&#39;d hate them anyway. Humans do love some of that cleverness. Rails developers adore methods that spring into existence from a naming convention, and Lisp programmers will tell you that macros are the whole point. But clever code is, almost by definition, code whose meaning differs from its appearance, and an AI goes by appearances. It has read more code than any of us, and it reads and writes what it has seen most. Show it an implicit conversion, an overloaded operator, a macro or a monkey patch, and it will read each one as whatever it looks like, confidently, and be wrong in exactly the interesting places.&lt;/p&gt;
&lt;p&gt;My ideal language taught me this, without even existing. In late 2025, I had agents write examples against my specification, which at the time insisted on lowercase type names and &lt;code&gt;--&lt;/code&gt; comments, among other idiosyncrasies. They drifted straight back to the syntax they&#39;d been trained on. An AI doesn&#39;t respect your novelty. It quietly averages it away. And nothing caught it, because nothing in my toolchain was checking. For a while there, my nonexistent language was trust-only.&lt;/p&gt;
&lt;h2&gt;Friction is feedback&lt;/h2&gt;
&lt;p&gt;Now look at Rust from the AI&#39;s side of the table.&lt;/p&gt;
&lt;p&gt;The borrow checker that humans spend months fighting is, to an AI, a strict senior reviewer that never gets tired and never lets anything slide. Together with the rest of rustc, it rejects whole categories of mistakes before anything runs: the use-after-free, the data race, the null that wasn&#39;t supposed to be there, the &lt;code&gt;match&lt;/code&gt; that forgot a case. And when it says no, it usually says what to do instead: &amp;quot;help: consider borrowing here&amp;quot;. Sometimes it even says &amp;quot;help: consider cloning the value if the performance cost is acceptable&amp;quot;, which makes rustc the only compiler I know of that will negotiate on price.&lt;/p&gt;
&lt;p&gt;The AI takes that offer every time, of course, and when it gets desperate, it reaches for &lt;code&gt;.unwrap()&lt;/code&gt; and &lt;code&gt;unsafe&lt;/code&gt;. But in Rust, trust is the exception, and it has to be asked for in writing. Every &lt;code&gt;.unwrap()&lt;/code&gt; is a bet placed in plain sight. &lt;code&gt;unsafe&lt;/code&gt; is a keyword you can grep for, and even inside an &lt;code&gt;unsafe&lt;/code&gt; block, the borrow checker goes on checking every reference. In Python, trust is the default, and it&#39;s the checking you have to ask for.&lt;/p&gt;
&lt;p&gt;A human fighting the borrow checker gets fed up after the tenth attempt and goes home. Whatever love and admiration they had for Rust is gone by then. Then again, these aren&#39;t the people who fill out Stack Overflow surveys. Who still uses Stack Overflow, anyway? The same people who still have Slashdot as their home page?&lt;/p&gt;
&lt;p&gt;An AI shrugs and makes the eleventh attempt, and the twelfth, at three in the morning, for pennies, without once losing its temper. What a human experiences as friction, an AI experiences as feedback.&lt;/p&gt;
&lt;p&gt;That&#39;s why, when I restarted my ideal language for AI programmers, I picked Rust as its first backend and as its first oracle, the independent judge of whether a program&#39;s ownership really holds up. I don&#39;t enjoy reading Rust. I just couldn&#39;t find a stricter reviewer. My own design notes concede that lifetimes are &amp;quot;where agents writing Rust spend most of their failed attempts&amp;quot;. But every one of those failures is caught at compile time, which is the only time you ever want anything to fail.&lt;/p&gt;
&lt;p&gt;So yes: Rust is write-only for humans, and it&#39;s the perfect language for AI. That makes Rust the natural predator in today&#39;s language pool. And when a human does need to understand some gnarly piece of it, the AI can explain it to them.&lt;/p&gt;
&lt;p&gt;Rust lovers, this is the good news I promised you. Your language won. Not that any of you have any business writing it any more. You&#39;re competing in an overflowing market of cheap AI labor that is faster and better than you even think you are.&lt;/p&gt;
&lt;p&gt;I do still have to review what the AI wrote, so haven&#39;t I just moved the problem? Not really. Most of what makes Rust hard to read is proof: lifetimes, ownership modes, trait bounds. Those are claims the compiler has already checked, and nobody needs to re-check a checked proof. What&#39;s left for me is the one question a human is actually needed for: is this what we wanted?&lt;/p&gt;
&lt;p&gt;There&#39;s a difference between code that&#39;s hard to read because it&#39;s full of proofs and code that&#39;s hard to read because it&#39;s full of tricks. You can skip a checked proof. You can&#39;t skip a trick. Rust has tricks too, mind you. &lt;code&gt;?&lt;/code&gt; quietly converts your errors, and a macro can hide almost anything. It just has fewer of them, and most of them announce themselves.&lt;/p&gt;
&lt;p&gt;Now compare that with reviewing AI-written Python. Python is far easier to read, and I&#39;d have to read every line of it, because nothing else has. In a trust-only language, the human reviewer is the type checker. And as &lt;a href=&quot;/v1/working-with-ai/programming-was-my-art&quot;&gt;I&#39;ve established elsewhere&lt;/a&gt;, the human reviewer is the slowest participant in the whole process.&lt;/p&gt;
&lt;p&gt;I&#39;d rather review AI-written Rust than AI-written Python, and I can barely read Rust.&lt;/p&gt;
&lt;h2&gt;The map&lt;/h2&gt;
&lt;p&gt;Put the two ideas together and you get a map. One axis asks whether a human can read the code. The other asks whether the language takes your word for it.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Humans can read it&lt;/th&gt;
&lt;th&gt;Write-only for humans&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Checks your work&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Go, C#, Java, Kotlin, Swift&lt;/td&gt;
&lt;td&gt;Rust, Haskell, Scala&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Takes your word for it&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Python, JavaScript, Ruby, PHP, C&lt;/td&gt;
&lt;td&gt;Perl, bash, APL, C++, Excel&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Top left&lt;/strong&gt; is the boring corner. Humans have spent years mocking these languages for their ceremony: Java&#39;s &lt;code&gt;AbstractSingletonProxyFactoryBean&lt;/code&gt;, Go&#39;s &lt;code&gt;if err != nil&lt;/code&gt; on every third line. But ceremony costs an AI next to nothing, and a reviewer can skim straight past it. The boring languages may be having the last laugh. They&#39;re just typing it out in full.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Top right&lt;/strong&gt; is Rust&#39;s home: write-only for humans, excellent for AI, and fine for a reviewer, as long as the hard parts are proofs rather than tricks. Haskell lives there too, as long as nobody imports lens. Scala lives there uneasily, because its implicits are tricks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bottom left&lt;/strong&gt; is where humans are happiest. They love these languages for exactly the reason they&#39;re weak for AI: they never argue.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bottom right&lt;/strong&gt; is Perl&#39;s home, and it has company. Write-only means no human can check the code. Trust-only means no compiler can. That leaves exactly one party checking it: the AI that wrote it.&lt;/p&gt;
&lt;h2&gt;The trade is off&lt;/h2&gt;
&lt;p&gt;Write-only languages trade the reader for the writer. Trust-only languages trade the checker for the writer. Both trades were made for the benefit of a writer with Larry Wall&#39;s three great virtues: laziness, impatience and hubris.&lt;/p&gt;
&lt;p&gt;The new writer is never lazy and never impatient. It does have the hubris. That&#39;s what the compiler is for.&lt;/p&gt;
&lt;p&gt;So the trade is off. The writer doesn&#39;t need the favors any more. The reader and the checker do. If I were designing a language from scratch today, and I am, I&#39;d want Python&#39;s readability with Rust&#39;s suspicion.&lt;/p&gt;
&lt;p&gt;My ideal language keeps its syntax boring, borrowed from Rust, Swift and Kotlin, because that&#39;s what agents already write and reviewers already read. It spends its novelty on what the compiler can check. Every failure is in the signature. Every wait has a deadline. Anything that asks for trust has to say so, in a closed vocabulary, with a reason, and the toolchain always shows it to a human. Every error that says no also says what to do instead, because a rule that only says no gets routed around.&lt;/p&gt;
&lt;p&gt;A language for machines to write and people to read. Neither write-only nor trust-only.&lt;/p&gt;
&lt;p&gt;Back in 1972, a joke chip made it into a data book because it passed through a long chain of approvals and none of them checked anything. Fifty-four years later, we can automate that chain. One AI writes the code, another reviews it, a third writes the tests, and in a trust-only language, every link in the chain takes the previous one&#39;s word for it.&lt;/p&gt;
&lt;p&gt;We&#39;ll find out it&#39;s broken the same way Signetics did.&lt;/p&gt;
&lt;p&gt;When the customers start calling.&lt;/p&gt;
&lt;p&gt;This time, they won&#39;t be asking for pricing.&lt;/p&gt;
</content>
  </entry>
</feed>

