可改进的编程语言
A Perfectable Programming Language

原始链接: https://alok.github.io/lean-pages/perfectable-lean/

在一个派对上,在悉尼·冯·阿克斯的提问下,作者列出了40种编程语言,最终宣布Lean是“最好”的——不是因为它*是*完美的,而是因为它*可以*被完善。这源于Lean能够在语言自身内部正式验证属性,将代码分析转化为可证明的过程。 作者将Lean与那些事后添加类型系统(如PHP和Python)或采用复杂变通方法(如C++模板)的语言进行对比,认为Lean的依赖类型提供了一种从根本上“正确”的方法。这不仅限于类型,还包括强大的定理证明基础设施和无缝的元编程能力,并通过一个用于井字游戏的自定义语法进行了演示。 Lean的主要优势包括速度(通过可证明的代码等价性进行优化潜力)和不断壮大的社区,这使其与Coq、Idris和Agda等竞争对手区分开来。作者认为定理证明是编程的自然演进,而Lean编程能力和形式化验证的独特结合使其成为一种强大的重构工具,尤其是在人工智能时代。文章本身就是用Lean代码编写的,突显了它的多功能性。

## 黑客新闻讨论:可完善的编程语言 (Lean) 最近黑客新闻的讨论围绕着 [alok.github.io](https://alok.github.io) 上题为“可完善的编程语言”的文章,特别是 Lean 编程语言和定理证明器。 用户们争论了 Lean 的实用性,一些人指出其不断增长的体积(现在解压后超过 2.5GB,从之前的 15MB 增长)是相对于 Coq 和 Agda 的一个显著缺点。另一些人则称赞 Lean 4 是一款一流的函数式语言,但也承认其在非构造性公理方面存在局限性。 对话涉及了 Lean 在工业中的应用(F# 在保险领域,可能取代 Haskell),其独特的博客实现方式(使用 Lean 代码构建,并通过 Verso 渲染),以及可用的编辑器支持(VS Code、Emacs、Neovim)。 一个相关的讨论围绕着在线写作中非常规的大小写风格展开,一些人将其归因于非正式的交流习惯,而另一些人则提倡正确的语法。最后,用户们还评论了在线讨论中脏话似乎有所增加。
相关文章

原文

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 {
  return 5;
}
def returnFive (unused variable `x` Note: This linter can be disabled with `set_option linter.unusedVariables false`x : Int) : Int := 5 theorem returnFive_eq_five (x : Int) : returnFive x = 5 := rfl example (a : Int) : 6 = returnFive a + 1 := a:Int6 = returnFive a + 1 a:Int6 = 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 {stx}" 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 {b.raw.getNumArgs}" 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 {squares.size}" 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 X | O | _ _ | X | _ O | _ | X#eval board! X | O | _ _ | X | _ O | _ | X Win X#eval getGameStatus (board! X | X | X O | O | _ _ | _ | _)
X | O | _
_ | X | _
O | _ | X
Win X

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 Win X#eval getGameStatus «🎮 tic tac toe 🎮»
Win X

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:IntreturnFive a + 1 = 6 a:Int5 + 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.

联系我们 contact @ memedata.com