🔥 Read this trending post from Hacker News 📖
📂 **Category**:
📌 **What You’ll Learn**:
at a party, Sydney Von Arx asked if i could name 40 programming languages.
yeah, that’s the bay for you.
racket, agda, clean, elm, typescript, sh, ASP, verilog, javascript, scheme,
rust, nim, intercal, sed, isabelle, visual basic, zsh, alokscript, coq, idris,
hack, prolog, whitespace, purescript, go, odin, haskell, python, tcsh, unison,
clingo, bash, java, zig, cyclone, php, awk, c, actionscript, c++.
But Lean is the best.
why?
because it’s perfectable.
it’s not perfect, but it is perfectable.
you can write down properties about Lean, in Lean.
the whole edifice of these facts and properties shall be known as progress.
in every language, you eventually wanna say stuff about the code itself.
like here’s a function that always returns 5, but in almost no language can you
really use that fact in a way that the language itself helps you with.
function returnFive(x : number) : number 💬
def returnFive (x : Int) : Int := 5
theorem returnFive_eq_five (x : Int) : returnFive x = 5 := rfl
example (a : Int) : 6 = returnFive a + 1 := a:Int⊢ 6 = returnFive a + 1
a:Int⊢ 6 = 5 + 1
All goals completed! 🐙
languages without types tend to grow them, like
PHP in 7.4 and Python type annotations, and the general trend towards TypeScript
and Rust.
inevitably, people want to push types. even Go. C++ templates are the ultimate
example. if it can be computed at compile time, at some point someone wants
to, like Rust’s ongoing constification.
but the easiest way to do anything is properly. doing it properly is basically
dependent types. there are fancier things than them, but like
Turing-completeness, dependent types can get you there. hence perfectable.
on top of the types, you want infrastructure for showing 2 types are equal/not
equal. this is basically a theorem prover. any dependent language can become a
theorem prover, but it needs to grow the nice API we call “theorem proving
infrastructure”.
that’s half the story. the semantics half. the syntax half is metaprogramming
and custom syntax.
metaprogramming
most languages have no facility for this, or it’s a bit awkward, like Rust’s
procedural macros.
Lean is freakishly seamless. here’s tic-tac-toe with a custom board notation:
inductive Player where
| X
| O
deriving BEq, Inhabited
inductive Square where
| empty
| occupied (player : Player)
deriving BEq, Inhabited
@[simp] def boardSize : Nat := 9
structure Board where
squares : Array Square
deriving BEq
now the custom syntax:
declare_syntax_cat tttCell
syntax "X" : tttCell
syntax "O" : tttCell
syntax "_" : tttCell
declare_syntax_cat tttRow
syntax (name := tttRowRule) tttCell "|" tttCell "|" tttCell : tttRow
declare_syntax_cat tttBoardSyntax
syntax tttRow tttRow tttRow : tttBoardSyntax
private def elabTttCell (stx : Lean.Syntax) : Lean.Elab.Term.TermElabM Square :=
match stx with
| `(tttCell| X) => pure (.occupied .X)
| `(tttCell| O) => pure (.occupied .O)
| `(tttCell| _) => pure .empty
| _ => Lean.throwError s!"unsupported cell syntax ⚡"
open Lean Elab Term in
elab "board! " b:tttBoardSyntax : term => do
let mut squares : Array Square := #[]
unless b.raw.getNumArgs = 3 do
Lean.throwError s!"Expected 3 rows, got ⚡"
for rowIdx in [:3] do
let row := b.raw.getArg rowIdx
unless row.isOfKind `tttRowRule do
Lean.throwError s!"malformed tttRow"
let cells := #[row.getArg 0, row.getArg 2, row.getArg 4]
for cell in cells do
squares := squares.push (← elabTttCell cell)
unless squares.size = boardSize do
Lean.throwError s!"internal error: expected 9 squares, got ⚡"
let squareTerms ← squares.mapM fun sq =>
match sq with
| .empty => `(Square.empty)
| .occupied .X => `(Square.occupied Player.X)
| .occupied .O => `(Square.occupied Player.O)
let arrSyntax ← `(#[$squareTerms,*])
let boardTerm ← `(Board.mk $arrSyntax)
Lean.Elab.Term.elabTerm boardTerm none
#eval board!
X | O | _
_ | X | _
O | _ | X
#eval getGameStatus (board!
X | X | X
O | O | _
_ | _ | _)
this lets you design APIs in layers and hide them behind syntax. plus the
interpretation of the syntax can be swapped easily. Lean’s type system helps a
bit with the metaprogramming (but i would love to see meta-metaprogramming with
some sort of modality for better infra around Lean Syntax).
def «🎮 tic tac toe 🎮» : Board := board!
X | O | X
O | X | O
X | _ | O
#eval getGameStatus «🎮 tic tac toe 🎮»
doing this properly just is a theorem prover. theorem proving comes about from
convergent evolution in programming.
speed
this is the biggest one. slow languages suck. why use a computer then.
Lean could be faster. it’s not Rust-fast, but it has a
very high ceiling for optimization thanks to the ability to show 2
pieces of code equal.
theorem five_plus_one_eq_six : ∀ a : Int, returnFive a + 1 = 6 := ⊢ ∀ (a : Int), returnFive a + 1 = 6
a:Int⊢ returnFive a + 1 = 6
a:Int⊢ 5 + 1 = 6
All goals completed! 🐙
Leo de Moura seems convinced of the need for this too, enough that backward
compat is going by the wayside. thankfully in AI world rewriting code is a lot
easier, and a theorem prover is the ultimate refactoring tool (how could it not
be?)
community
Lean is the only language in its class that’s actually gaining traction.
Coq, Idris, Agda — none of them are really competing anymore.
Idris would otherwise count as a real programming language that’s also a prover,
but the community never reached critical mass.
F* could count too, but its community is a rounding error.
Lean is the one with raw programming ability that’s also a theorem prover,
and it’s growing.
this blog post is itself Lean code.
For Eliza Zhang, who bet I couldn’t write a web app in C in one week using only the standard library. She was right. I didn’t know what any of those words meant. But I said the fuck I can’t, and that’s how I got into coding.
{💬|⚡|🔥} **What’s your take?**
Share your thoughts in the comments below!
#️⃣ **#perfectable #programming #language #Soter**
🕒 **Posted on**: 1776035629
🌟 **Want more?** Click here for more info! 🌟
