PapersWeLove
Converting a minimal three-line Lisp interpreter into a bidirectional relation allows logic engines to run execution backward, transforming standard evaluation into automated program synthesis.
AI-assisted summary of William Byrd on "The Most Beautiful Program Ever Written" [PWL NYC] — original source. Check the original for full context.
Source published:
Key ideas
- William Byrd demonstrates that a complete, Turing-complete Lisp interpreter can be reduced to just three essential constructs: variable lookup, lambda abstraction, and function application. (45:18)
- Alan Kay famously identified the Lisp-in-Lisp interpreter on page 13 of the LISP 1.5 manual as the 'Maxwell's equations of software', functioning as an extensible laboratory for language semantics. (5:27)
- Implementing a metacircular evaluator as a relational interpreter in miniKanren turns evaluation into a bidirectional constraint problem, enabling automated program synthesis and quine generation. (61:02)
- The prototype synthesizer Barliman leverages an optimized miniKanren engine running roughly 10 million times faster than naive implementations, synthesizing complete recursive functions like append from scratch. (75:08)
Detailed notes
## Background This presentation was published on May 24, 2017, by the PapersWeLove YouTube channel. It features research assistant professor William E. Byrd exploring his perspective on the "most beautiful program ever written": a metacircular Lisp interpreter implemented in Lisp, and the profound computational ideas derived from its architecture.
## TL;DR Byrd dissects the fundamental three-line core of a Lisp interpreter—variable lookup, lambda abstraction, and procedure application—arguing that this minimal structure is the true "Maxwell's equations of software." He tracks the evolution of this interpreter from a simple calculator to a powerful laboratory for studying programming language semantics, such as environment scoping and recursion. The talk culminates in a demonstration of how this metacircular evaluator can be encoded as a relational program using miniKanren, enabling automated program synthesis and quine generation.
## Atmosphere & style The presentation feels like an impassioned, high-energy lecture delivered by a true academic devotee who balances technical rigor with a playful, almost cult-like reverence for his subject matter. Byrd’s narrative style is conversational and urgent, blending dry humor and self-deprecating warnings with a deep-seated belief in the elegance of extreme minimalism. It is an intellectual, stimulating experience that manages to make abstract concepts like reflective towers and relational constraints feel both profoundly foundational and thrillingly experimental.
## Bulletin - [45:18](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=2718s) William Byrd demonstrates that a complete, Turing-complete Lisp interpreter can be reduced to just three essential constructs: variable lookup, lambda abstraction, and function application. - [05:27](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=327s) Alan Kay famously identified the Lisp-in-Lisp interpreter on page 13 of the LISP 1.5 manual as the 'Maxwell's equations of software', functioning as an extensible laboratory for language semantics. - [01:01:02](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=3662s) Implementing a metacircular evaluator as a relational interpreter in miniKanren turns evaluation into a bidirectional constraint problem, enabling automated program synthesis and quine generation. - [01:15:08](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=4508s) The prototype synthesizer Barliman leverages an optimized miniKanren engine running roughly 10 million times faster than naive implementations, synthesizing complete recursive functions like append from scratch.
## Key takeaways - [00:00](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=0s) Dan Friedman's pedagogical philosophy holds that profound computational ideas should fit on a 3x5 index card before expanding into a book. - [02:33](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=153s) Minimal program size does not imply semantic simplicity, as demonstrated by the unsolved 3-line Collatz conjecture cited by Paul Erdős. - [05:27](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=327s) A Lisp-in-Lisp metacircular interpreter provides a baseline laboratory for exploring stores, evaluation strategies, and continuation-passing style (CPS). - [08:30](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=510s) Reflective towers allow stacked interpreters where lower layers can inspect and alter the semantics of higher layers, as seen in languages like Black. - [17:00](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=1020s) Symbols in Scheme provide interned, atomic representations that allow fast pointer-equality comparisons rather than string traversals. - [27:17](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=1637s) The pmatch macro library facilitates structural pattern matching on s-expressions, eliminating boilerplate list-accessor operations like car and cdr. - [45:18](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=2718s) Primitive operations like arithmetic and conditionals serve merely as convenience forms; the Turing-complete computational core consists solely of variable lookup, lambda abstraction, and application. - [45:18](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=2718s) Representing environments as higher-order Scheme procedures allows variable lookups to be resolved cleanly through nested lexical closures. - [53:57](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=3237s) Recursion can be realized entirely without named definitions or recursion primitives by using self-application and fixed-point combinators. - [59:38](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=3578s) Standard evaluation treats equality as a unidirectional reduction, whereas mathematical equality can be interpreted bidirectionally. - [01:01:02](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=3662s) Encoding operational interpreter semantics relationally in miniKanren allows querying for missing source expressions given expected outputs. - [01:09:02](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=4142s) Relational interpreters can naturally synthesize quines—expressions that evaluate directly to themselves—without domain-specific heuristics. - [01:15:08](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=4508s) The 'Genie Problem' in program synthesis arises when an engine satisfies a single test case with trivial, hard-coded constants instead of generalized logic. - [01:18:16](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=4696s) Supplying gensym inputs forces the relational synthesizer to implement structural recursion over list elements rather than exploiting literal values. - [01:21:12](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=4872s) Relational synthesis functions like continuous test-driven development, where miniKanren acts as an automated theorem prover detecting mutual contradictions between tests and code. - [01:27:36](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=5256s) Pruning the combinatorial search space using human stylistic priors—such as expecting definitions to wrap lambdas and conditionals—dramatically accelerates program synthesis.
## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=0s) Dan Friedman, Index Cards, and the Five-Line Program
William Byrd opens by recounting the winding path that led him to graduate school. After stints as a middle school teacher and running a summer camp, he eventually earned a computer science degree and worked as a Java developer. Becoming fed up with the boilerplate world of Java and JavaScript, Byrd moved to Indiana University to study under Dan Friedman, the legendary computer scientist behind classic texts like *The Little LISPer*, *The Little Schemer*, and *The Seasoned Schemer* (with whom Byrd would later co-author *The Reasoned Schemer*). Just a few weeks into his first semester in Friedman's class, Friedman introduced him to a program that altered the entire trajectory of his life. Byrd explains that while many engineers find satisfaction in building massive artifacts—such as a 100,000-line compiler or an operating system kernel with two million lines of code—he gravitates in the absolute opposite direction, seeking extreme compression and conceptual clarity.
Friedman instilled in him a strict discipline centered around the physical index card: every profound computational idea should fit onto a 3x5 card, or at most a 4x6 card. As Byrd describes Dan Friedman's ethos: “If you can't fit down the idea on a 4x6 card then think harder that's sort of Dan's philosophy right and ideally you get it down to the 3x5 card and then you write a little book on it and so that's that's kind of his his way of thinking.” Byrd has spent the last twelve to thirteen years pondering the implications of this five-line Lisp interpreter, expects to spend the rest of his life doing so, and admits that even now, merely thinking about it gives him genuine "nerd chills."
- Byrd's pre-academic career spanned middle school teaching, running summer camps, and commercial enterprise programming in Java and JavaScript. - Dan Friedman's index-card rule states that if an algorithm cannot fit onto a 4x6 index card, the programmer has not thought hard enough yet; condensing it to a 3x5 card earns the right to write a book around it. - Byrd compares the minimalist index card aesthetic favorably against sprawling industrial codebases containing millions of lines.
---
[02:33](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=153s) The Illusion of Understanding and the Collatz Warning
Byrd expresses genuine anxiety about presenting this material to a sophisticated audience. Programmers who have spent years in the field—reading *Structure and Interpretation of Computer Programs* (SICP), studying the lambda calculus, and implementing various interpreters—often fall prey to intellectual complacency. When developers believe they have fully mastered an elementary concept, they stop looking for deeper lessons. Byrd combats this complacency by deliberately revisiting fundamental concepts every few years to unearth new perspectives, arguing that the deepest ideas in computer science continually reveal fresh angles under scrutiny.
To illustrate how terrifyingly complex a miniature program can be, Byrd poses the Collatz conjecture (or the $3n + 1$ problem) to the audience. Even though the Collatz function can be written in two or three trivial lines involving only basic multiplication, addition, and integer division by two, the world's greatest mathematicians have failed for decades to prove that it terminates for every positive integer. Byrd underscores the scale of humility required: “Paul Erdős one of the great mathematicians at all time at one point said mathematics is not ready for the Collatz problem right so that's a three-line program who stumped some of the greatest minds in history so I'm talking about like a five-line program okay so that should give you pause that should give you pause and we all have to have a healthy amount of respect for the the 3x5 card program.”
- Seasoned programmers risk missing new insights because they assume that familiar topics like basic interpreters and the lambda calculus hold no more surprises. - The Collatz conjecture takes a positive integer $n$: if odd, compute $3n + 1$; if even, divide by 2. It remains unproven whether the sequence always reaches 1 for all positive integers. - Paul Erdős famously stated that contemporary mathematics was simply not yet equipped to solve the Collatz problem, serving as proof that program line count does not correlate with semantic simplicity.
---
[05:27](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=327s) The Lisp-in-Lisp Interpreter: Maxwell's Equations of Software
The focal program of the talk is an interpreter for Lisp written in Lisp—a metacircular evaluator. For novices, the code initially looks like a bewildering tangle of parentheses and nested lambda expressions. Once a developer traces through enough evaluations to understand how environments and abstractions operate, the interpreter reveals itself as an extraordinarily fertile ground for experimentation. Byrd describes it as an extensible laboratory where one can systematically apply correctness-preserving transformations: threading an explicit store argument to model state and mutable boxes, modifying evaluation rules to switch between call-by-value, call-by-name, and call-by-need (lazy evaluation), or converting the interpreter into Continuation-Passing Style (CPS).
Byrd jokingly dubs CPS the "gateway drug of program transformations," noting that once an interpreter is CPSed once, one can CPS it a second time to uncover delimited continuations—at which point "you're cooking with gas." Drawing a historical comparison, Byrd points out that computer pioneer Alan Kay had a transformative epiphany when he encountered the Lisp interpreter defined at the bottom of page 13 of the *LISP 1.5 Programmer's Manual*. Byrd explains: “Alan Kay said that when he first saw the Lisp interpreter written in Lisp at the bottom of page 13 of the Lisp 1.5 manual right it changed his life and he realized these are Maxwell's equations of software.” Just as James Clerk Maxwell synthesized electricity and magnetism into four compact mathematical equations that take a lifetime of study to unpack, this five-line Lisp interpreter condenses the fundamental nature of computation into a handful of symbols.
```mermaid flowchart TD accTitle: Extensions to the Lisp Metacircular Interpreter accDescr: Diagram illustrating various semantic and transformational paths branching out from the core 5-line Lisp-in-Lisp interpreter.
subgraph Core ["Minimal Foundation"] Interpreter["5-Line Lisp-in-Lisp<br/>Interpreter (Page 13)"] end
subgraph Semantics ["Evaluation Semantics"] Store["Store Argument<br/>(Boxes & Mutation)"] EvalOrder["Evaluation Strategies<br/>(Call-by-Value / Name / Need)"] end
subgraph Transformations ["Transformations"] CPS1["First CPS Transformation<br/>(First-class Continuations)"] CPS2["Second CPS Transformation<br/>(Delimited Continuations)"] end
Interpreter --> Store Interpreter --> EvalOrder Interpreter --> CPS1 CPS1 --> CPS2 ```
- The *LISP 1.5 Programmer's Manual* (authored by John McCarthy, Paul W. Abrahams, Daniel J. Edwards, Timothy P. Hart, and Michael I. Levin) contained the complete definition of Lisp evaluation via `eval` and `apply` on page 13. - Alan Kay famously compared this definition to "Maxwell's equations of software," representing the ultimate distillation of universal computation. - By mutating the interpreter's signature, developers can natively introduce mutable state, alternate evaluation strategies, and first-class or delimited control flow.
---
[08:30](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=510s) Reflective Towers and the Gateway to Programming Languages
Beyond standard semantic variations, Byrd points to far more mind-bending extensions of the metacircular evaluator, highlighting reflective towers of interpreters. In a basic tower, an interpreter evaluates another interpreter, which evaluates another, stacked upward. The scenario becomes wild when the tower is conceptualized as infinitely tall, and an interpreter running at a lower level reaches upward into the interpreter interpreting it to alter that evaluator's rules—thereby dynamically rewriting its own execution semantics from within. Byrd recounts that Dan Friedman himself flatly refuses to touch reflective towers anymore because the conceptual gymnastics "melted his brain too much." Byrd recommends Kenichi Asai's research on the reflective language Black and Nada Amin's 2014 Strange Loop keynote, *Programming Should Eat Itself*, for those daring enough to explore towers.
Byrd's goal is to connect these reflective concepts with relational programming and push interpreter mechanics so far that even seasoned lambda calculus veterans get a scared look on their faces. Beyond reflective towers, interpreters can share internal representations with type inferencers or be converted into relational search engines. Summing up the boundless depth packed into this tiny program, Byrd asserts: “Just like CPS is the gateway drug of program transformations lisp in lisp is the gateway drug of the whole world of programming languages I think.” Preparing to write out Friedman's specific five-line variant of the interpreter, Byrd invokes Paul Erdős's whimsical philosophical notion of "The Book"—a divine ledger where God records the single most elegant, beautiful proof for every mathematical truth—proposing that this particular interpreter belongs on its pages.
- Reflective towers allow an interpreter at level $n$ to inspect and alter the code of level $n+1$, changing the language semantics while running. - References for reflective towers include Kenichi Asai's work on the Black language and Nada Amin's Strange Loop 2014 keynote titled *Programming Should Eat Itself*. - Paul Erdős's concept of "The Book" posited that God maintains a volume containing only the most perfect, beautiful mathematical proofs; Byrd positions the five-line Lisp evaluator as computer science's equivalent entry.
[11:15](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=675s) The Program in "The Book" and Reading S-Expressions
Invoking the famous mathematical concept attributed to Paul Erdős—where God keeps a celestial book containing the most elegant proof for every theorem—William Byrd asserts that this talk is about the equivalent masterpiece in computing: the metacircular Lisp interpreter. As he puts it: “I've written hundreds of interpreters and I've read lots of books on interpreters, but this to me is the one in the book; everything else is a pale imitation.”
Polling the audience to see who can read symbolic expressions (s-expressions) or parenthetical syntax, Byrd finds about an even fifty-fifty split. He reassures beginners that he will teach them the fundamentals as they go. Acknowledging that the talk is packed with concepts deep enough to make one's head spin, he jokes that if anyone gets dizzy, they can simply close their eyes, lean forward until the room stops spinning, and rejoin the flow of ideas. He promises that getting even an inkling of these ideas will leave listeners with a burning passion to learn the rest—or at least discuss it over a milkshake afterward.
[12:40](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=760s) Chez Scheme, the REPL, and Trees
Byrd opens Chez Scheme—noting with enthusiasm that it is now open-source—and introduces the interactive REPL (stressing that “it's pronounced reppel not reple”). He explains that in high-performance implementations like Chez Scheme, interactively typed expressions are analyzed on the fly to determine whether direct interpretation or JIT-style machine-code compilation and evaluation will be faster.
```mermaid flowchart TD accTitle: REPL Evaluation Pipeline in Chez Scheme accDescr: An s-expression entered in the REPL is evaluated or compiled to native machine code on the fly. A["User types S-Expression"] --> B{"Chez Scheme Analysis"} B -->|"Faster to interpret"| C["Interactive Evaluation"] B -->|"Faster to compile"| D["Compile directly to Machine Code"] C --> E["Return Result to REPL"] D --> E ```
Walking through the basics, Byrd demonstrates that self-evaluating atoms like `5` evaluate to `5`. He then demonstrates a list containing `5`, `(5)`, and lists containing multiple elements like `(5 6)`. He proudly points out the absence of syntactic clutter: “Notice that I did not defile my program with commas or anything else like that.” Nesting lists within lists creates deeper parenthetical structures, which Byrd reveals is simply another name for an inductively defined data structure: a tree.
Connecting this structure to computing history, Byrd links s-expressions and trees to XML, its predecessor SGML, and the styling language DSSSL (Document Style Semantics and Specification Language): - DSSSL was essentially R4RS Scheme because recursive processing is the natural, inductive way to manipulate tree-structured data. - Programs themselves are represented as trees (abstract syntax trees), making a tree-manipulating language like Lisp the ideal tool for programs that manipulate other programs. - Byrd quips about Haskell developers insisting there are superior ways: “Well, they'll be people—the Haskellers will say they're better ways... but anyway, don't listen to them.” - For learning Scheme, he recommends *Lists and Lists*, an interactive fiction text adventure by Andrew Plotkin (zarf), where a genie teaches the player Scheme on an emulated Z-machine.
[17:00](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=1020s) Booleans, Quotation, and Symbols
Byrd transitions to data types beyond integers. In Scheme, `#t` represents true, while `#f` represents false. He quips that while `#t` is truth, “there is no representation of beauty.” Notably, in Scheme's conditional semantics, every single value in the language is considered true except `#f` itself—meaning the integer `5` acts as truth.
He then introduces the foundational, often controversial operator `quote` (written as the special form `(quote...)` or using the single-quote shorthand `'`). While self-evaluating literals like `5` do not require quotation, quoting an s-expression stops evaluation in its tracks, treating the code literally as data: - `(quote (5 6))` and `'(5 6)` evaluate to the literal two-element list `(5 6)`. - Quoting `()` evaluates to the empty list.
Next, Byrd introduces symbols, contrasting them with other languages: “And if you program in Java, I'm sorry, you don't get symbols. If you program in many languages, you don't get symbols, but you do have symbols in a Scheme, in most Lisps.” A quoted word like `'milkshake` evaluates to the symbol `milkshake`. Unlike strings, symbols are atomic, metaphorical representations of objects that cannot be torn apart. Because symbols are interned, comparing them for equality internally reduces to a fast pointer comparison, obviating the need to use crude integer constants (as one might in C) to represent discrete names.
[19:48](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=1188s) Predicates, Cons, and Conditionals
Moving to program execution, Byrd explains how parenthesized lists represent function calls. When an expression starts with an open parenthesis, the first element represents the procedure being called, unless it is a special form with customized evaluation rules. Scheme predicates end by convention with a question mark and return a boolean: - `(null? '())` evaluates to `#t`. - `(null? 5)` evaluates to `#f`.
Lists can be constructed literally via quotation, through the `list` procedure (`(list 3 4 5)`), or via the foundational primitive `cons` (short for *construct*): - `cons` prepends an element onto an existing list: `(cons 5 '())` produces `(5)`, and chaining `(cons 6 (cons 5 '()))` produces `(6 5)`. - Byrd notes historical papers on lazy evaluation and `cons`: Daniel P. Friedman and David S. Wise’s 1976 paper *"CONS Should Not Evaluate its Arguments"* (one of the earliest formulations of Haskell-style laziness), and Henry Baker's playfully titled rebuttal paper, *"CONS Should Not CONS its Arguments"*.
To make choices in code, Byrd contrasts two conditional forms: 1. The three-part `if` special form: `(if test consequent alternative)`. In `(if (null? 5) 6 7)`, the condition is false, evaluating to `7`. 2. The multi-branch `cond` form, which avoids messy nested `if` statements by evaluating pairs of conditions and expressions sequentially until a condition evaluates to true.
Finally, Byrd highlights that standard arithmetic operators like `+` and `*` are simply procedures bound to symbols. Because Scheme supports arbitrary-precision integers (bignums), procedures like `*` can compute astronomically large values seamlessly, setting the stage to define custom functions with `define`.
[23:37](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=1417s) Variables, Environments, and Lambda Calculus Mechanics
William Byrd transitions from basic values to binding names and building procedures. He defines a new variable using a food reference from earlier in the evening: `(define taro 'good)`. He explains the exact runtime mechanism: when typing `taro`, the variable is looked up in a structure called an *environment*, returning the bound value—the symbol `good`. He clarifies:
> “`define` just gives a name to some value.”
Next, he introduces functions (or procedures) through `lambda`. While he types `lambda`, his editor configuration automatically displays the Greek character `λ`. A `lambda` expression takes a list of formal parameters followed by a body. Demonstrating the identity function `(lambda (x) x)`, he explains:
> “This is just going to return its argument the value of this argument so this is the identity function.”
Byrd then unpacks function application syntax. In Scheme, an open parenthesis denotes a procedure call unless the first item is a special form like `quote` or `define`. Applying `((lambda (x) x) 5)` evaluates to `5`. This introduces the central distinction between an *expression* and its evaluated *value*. Byrd maps out the application lifecycle: - The operator expression (the "rator") is evaluated to yield a procedure. - The operand expression (the "rand") is evaluated to yield an argument value (here, `5` evaluates to itself). - The procedure application applies that procedure value to the argument value.
```mermaid flowchart TD accTitle: Procedure Evaluation Steps accDescr: Three-step pipeline showing how an operator and operand evaluate to a procedure and argument value before application. subgraph EvalPhase["1. Evaluation Phase"] Rator["Operator: (lambda (x) x)"] --> Proc["Procedure Value"] Rand["Operand: 5"] --> Val["Argument Value: 5"] end subgraph ApplyPhase["2. Application Phase"] Proc --> Apply["Apply Procedure to Argument"] Val --> Apply Apply --> Res["Final Result: 5"] end ```
Byrd defines a `double` procedure and jokes about type-safety complaints: `(double 'fun)` fails, prompting him to remark that “the Haskellers are going to give me grief,” whereas `(double 5)` works. He then shows the power of dynamic environments by running `(define define 5)`. Noting that one can also alter what `lambda` means, he observes:
> “That's where the real fun begins. I'm not going to talk about that—that's for the next talk.”
He restarts the Scheme REPL to clean his environment, playfully teasing the audience: “You’re not ready yet.”
---
[27:17](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=1637s) Pattern Matching with `pmatch`
Having covered Scheme fundamentals, Byrd introduces pattern matching, an extension made possible by Scheme's powerful hygienic macro system. He loads a pattern-matching macro library named `pmatch`:
> “`pmatch` is similar in spirit to our `cond`, except now we are going to match against an expression.”
To demonstrate syntax, he matches against a literal two-element list `'(3 4)`. A pattern `(,x,y)` binds `x` to `3` and `y` to `4`, and evaluates the body `(+ x y)` to produce `7`. Byrd clarifies the syntax: the commas indicate unquoting within an implicit quasiquote (`backquote`), establishing the pattern-matching bindings used throughout the interpreter.
```scheme (pmatch (list 3 4) [(,x,y) (+ x y)]) ;; Evaluates to: 7 ```
---
[29:10](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=1750s) Designing the Evaluator: `eval-expr`
Byrd creates a new file, `interp.scm`, to begin live-coding an interpreter from scratch. He asks the audience for naming suggestions. An audience member suggests "Fitzroy," leading to playful banter:
> “Fitzroy? How do you spell that? All right, so we have a name. This is our life now... thank you, derailed us all.”
He settles on a conventional name: `eval-expr`. He offers a critical safety tip: avoid naming the parameter `exp` because Scheme has a built-in exponential function named `exp`, and shadowing or misusing it produces confusing runtime errors:
> “It's important as a safety tip to not call that argument `exp`... because Scheme has an exponential function built in called `exp`, and if you type you get very [confusing] errors if you do it wrong, so I call it `expr`.”
When asked what comes after `define`, Byrd asserts:
> “When in doubt, the answer is `lambda`, right?”
He outlines what an evaluator does: it accepts an expression `expr` and produces a value.
---
[31:45](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=1905s) Evaluating Numbers and Avoiding Host Leaks
With `pmatch` set to inspect `expr`, Byrd considers the first expression type to support: numbers. In `pmatch`, a raw variable like `,n` matches anything, so a guard predicate is required: `[(,n (guard (number? n))) n]`. If `n` satisfies `number?`, the number evaluates directly to itself.
An audience member asks why not use `cond` directly with `(number? expr)`. Byrd agrees that for flat numbers `pmatch` adds extra syntax, but explains that as soon as compound forms with sub-structure (such as `lambda`) appear, pattern matching drastically simplifies destructuring. He jokes about avoiding primitive pair accessors: “Did I talk about `car`?... There's like a historical accident that involves some of the names in Scheme that have not yet been corrected intentionally, but we don't talk about that.”
Byrd loads `interp.scm` and tests the interpreter. When an audience member suggests evaluating `5`, Byrd points out a classic implementation trap:
> “`5`—you will go far, but that's the wrong answer. The right answer is `quote` of `5`, because we want to make sure that whatever expression we pass in is not evaluated by Scheme.”
To illustrate this pitfall, he demonstrates that running `(eval-expr (+ 3 4))` without quotation appears to work—returning `7`—not because their fledgling interpreter understands addition, but because the host Scheme interpreter evaluates `(+ 3 4)` before the evaluator ever receives it:
> “Well, our interpreter works so well that we already can handle addition! Wow, that's really impressive. And if you've written enough interpreters, you've run into this problem before. It's like: whoa, that's... I guess I can turn in my homework early!”
```mermaid flowchart TD accTitle: Host vs Guest Evaluation Leak accDescr: Compares passing an unquoted expression where host Scheme intercepts evaluation against passing a quoted expression to eval-expr. subgraph WrongCall["Unquoted Input: (eval-expr (+ 3 4))"] direction TB HostIn["Host Scheme evaluates (+ 3 4)"] --> Num7["Host yields 7"] Num7 --> EvalRun["eval-expr receives 7 directly"] EvalRun --> OutputFake["Returns 7 (False Success)"] end subgraph RightCall["Quoted Input: (eval-expr '(+ 3 4))"] direction TB HostQuote["Host Scheme sees quote"] --> ListIn["eval-expr receives list: '(+ 3 4)"] ListIn --> GuestParse["Guest Interpreter parses operator and arguments"] end ```
---
[35:06](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=2106s) Adding `add1` and Recursive Sub-Evaluation
To handle more than literal numbers, Byrd extends `eval-expr` with a clause for incrementing numbers: `add1`. He adds a structured pattern match:
```scheme [(add1,e) (add1 (eval-expr e))] ```
He highlights two design principles: 1. **Arbitrary Sub-expressions:** The sub-expression `e` is not restricted to a raw number; it can be nested arbitrarily (for instance, `(add1 (add1 5))`). 2. **Borrowing Host Semantics:** The interpreter delegates evaluation of sub-expressions by recursively invoking `(eval-expr e)`, and then uses the host Scheme's built-in `add1` procedure to compute the final result.
Byrd remarks:
> “I’m going to use Scheme's `add1` to implement `add1` in my language. That's cool, so let's try it.”
- Pattern matching `(add1,e)` unpacks the list structure automatically without manual calls to `car` and `cdr`. - Recursively calling `(eval-expr e)` ensures compound expressions nested inside `add1` evaluate to values before the outer host addition runs.
[36:33](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=2193s) Building Out the Boring Core: Arithmetic and Conditionals
William Byrd begins adding primitive operations to the interpreter, demonstrating how evaluating expressions requires recursive evaluation of sub-expressions. He starts with unary operations like `add1` and `sub1`, emphasizing that tests must be written properly so sub-expressions do not accidentally remain unevaluated. Moving to multiplication, he points out that it takes two sub-expressions, `e1` and `e2`, requiring two recursive evaluation calls. He jokes with the audience: “how many recursive calls do we have to make? Two. Two, right? Wow, this is like falling off a log.” Evaluating multiplication simply means taking the host language's `*` and applying it to the evaluated values of `e1` and `e2`.
Next, Byrd introduces conditionals with `if`, which has a test expression, a consequent, and an alternative. When asked how to evaluate `if`, an audience member suggests “if using if,” to which Byrd laughs, “you're catching on,” and jokes about using “copy and paste abstraction” (or perhaps a “copy and paste monad in Haskell”). An audience member asks whether evaluation short-circuits. Byrd clarifies that while boolean operators like `and` and `or` could benefit from short-circuiting, the fundamental semantics of `if` require evaluating the test first, after which only the consequent or only the alternative is evaluated—never both.
```mermaid flowchart TD accTitle: Conditional evaluation flow in eval-expr accDescr: Evaluation flow showing how if tests evaluate and branch to only one sub-expression.
subgraph EvalIf["Evaluating (if test consequent alternative)"] Test["Evaluate Test Expression"] Branch{"Test Result"} Cons["Evaluate Consequent"] Alt["Evaluate Alternative"] Result["Return Value"] end
Test --> Branch Branch -->|"#t (true)"| Cons Branch -->|"#f (false)"| Alt Cons --> Result Alt --> Result ```
[39:16](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=2356s) The Three Magic Lines of Code
Once the arithmetic and conditional operations are in place, Byrd dismisses them as trivial filler. He emphasizes that the real beauty of an interpreter does not live in primitives: “Basically this interpreter is pretty boring. All right, it's pretty boring, and the reason is we have not gotten to the beautiful part of the interpreter. The beautiful part of the interpreter has to do with three lines and only three lines.” He prompts the audience to guess those three lines. An audience member immediately answers `lambda`, prompting Byrd to cheer: “Lambda! That's the—I told you it's always the answer, right? Told you.”
The audience next guesses application, which Byrd confirms. But the audience struggles to identify the third line, guessing `eval`, `procedure`, `define`, and `quote`, all of which Byrd rejects with a grin: “Oh, wow. All right, I stumped y'all. Ha! All right, we'll figure it out in a minute.”
Byrd steps back to implement application first. In Scheme syntax, application consists of an operator (the "rator") and an operand (the "rand"). In the pattern matcher (`pmatch`), this case must be placed last so its catch-all pattern does not shadow other forms. In logic programming, clause order is ideally irrelevant, but `pmatch` matches clauses top-to-bottom. Byrd evaluates the rator and the rand recursively. The rator evaluates to a Scheme procedure and the rand evaluates to an argument value. Applying the procedure to the argument is accomplished simply by enclosing them together in Scheme parentheses. He highlights how easily this can be overlooked: “Some people have what might call parentheses blindness and they like just can't see that that is different than without those parentheses. But they are different. There, you know, there's no mistaking the difference in behavior.”
- The syntax `(rator rand)` matches an operator applied to an operand. - The rator is recursively evaluated to yield a procedure. - The rand is recursively evaluated to yield an argument value. - Host Scheme function call syntax `((eval-expr rator) (eval-expr rand))` performs the application.
[41:57](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=2517s) Variable Lookup and Passing an Environment
Turning to `lambda`, Byrd sets up a pattern matching `(lambda (,x),body)`. He asks the audience what `x` represents: outside the formal parameter list, an appearance of `x` in an expression is an identifier representing a variable reference. Its value cannot be known at interpretation time: “Don't know yet. When will you know?... At runtime when we've actually done the procedure application, that's right. We won't know until then.” If a procedure is invoked with `5`, `x` evaluates to `5`.
To map variables to runtime values, the interpreter must track bindings using an environment. Byrd explains that moving from isolated expressions to an environment-passing style transforms everything: “So you see right now we basically have a calculator. We have an expression, but there's no context of the expression. There's no notion of variable bindings that may be, you know, in play at this point.” By adding an `env` parameter, the interpreter gains context.
With the environment in play, Byrd reveals the mysterious third magic line that stumped the room earlier: variable lookup. When the audience suggests `cons` or `define`, Byrd pushes back, emphasizing that none of those are necessary for universal computation: “Cons? Nope. Cons is useful, but you actually don't need it. You only need these three lines. That's Turing-complete actually, it's amazing. We can delete all the other junk. The other stuff's junk, you don't need it.” He reminds the audience of foundational history: “You heard of this thing called a Turing machine? This came first. Lambda calculus came first. Gödel didn't believe Church that it was Turing-complete even though Turing-completeness hadn't been the term yet.”
- The three foundational constructs of Turing-complete computation in the interpreter are: 1. Procedure abstraction (`lambda`) 2. Procedure application (`(rator rand)`) 3. Variable lookup (referencing an identifier in `env`)
[45:18](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=2718s) Higher-Order Environments and Closures
Byrd details how variable lookup is implemented. Using a guard on the pattern matcher to test `(symbol? expr)`, any bare symbol is treated as a variable. He introduces an elegant representation choice: the environment is represented directly as a Scheme procedure of one argument (a variable name). To look up a variable `x` in environment `env`, the interpreter does not iterate through an association list; it simply calls the environment procedure with the variable: `(env x)`. Byrd acknowledges the austere minimalism of this design: “This is where it gets kind of weird. It's like kind of Spartan, right? I mean I'm just like got a procedure, there's only one useful thing a procedure can do: call it. Takes one argument, give it one argument, call it. That's it.”
Finally, Byrd addresses how `lambda` evaluates. In Scheme, evaluating a `lambda` yields a closure—a delayed computation holding onto its lexical environment until applied. Byrd asks how to represent a procedure, and the room replies with the recurring punchline: “Lambda! There you go. 60% of the time it works every time.”
The interpreter evaluates a source-level `lambda` expression by creating a host-level Scheme procedure `(lambda (arg)...)`. Byrd stresses that `arg` corresponds directly to the evaluated value of the operand (`rand`) supplied during procedure application. When that procedure is invoked, it evaluates the body of the `lambda` using a recursive call to `eval-expr`. However, it cannot use the original environment untouched; it must evaluate the body in an extended environment that associates the parameter `x` with the argument `arg`. And to the question of how to represent that extended environment procedure, the audience once again delivers the inevitable answer: `lambda`.
[49:10](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=2950s) Higher-Order Environments and Variable Lookup
William Byrd continues constructing the interpreter interactively with the audience, addressing how to extend the environment using a procedure. As the audience guesses, Byrd laughs in agreement: “it's a one trick pony but it's got a good trick.” The environment is extended using a `lambda` over an incoming lookup variable `y`. Byrd remarks that this is where the “nerd chills start,” because an environment is literally just a procedure of one argument. When looking up a variable, the environment procedure is invoked on that variable. Inside this extended environment procedure, an `if` expression checks identity using Scheme’s `eq?`: if the queried symbol `y` is equal to the bound parameter `x`, it returns the associated argument `arg`.
If the symbols are not equal, the lookup must recurse into the outer environment `env`, passing `y` along. Byrd emphasizes why it must be `y` rather than `x`: `y` is the variable currently being searched for as it travels into the innermost `lambda`. This implementation strategy is known as a higher-order representation of environments and procedures, where Scheme procedures directly represent the language's own procedures and environments. Byrd then updates the recursive calls in `eval-expr` to propagate the environment across clauses. He points out that while application looks trivial here, using data structures like association lists would push the environment-binding work out of the `lambda` clause and directly into the application line instead.
```mermaid flowchart TD accTitle: Higher-Order Environment Lookup accDescr: Diagram showing how a variable query is checked against the current binding or delegated to the outer environment.
subgraph EnvProc["Extended Environment: (lambda (y)...)"] Check{"(eq? y x)"} Hit["Return arg"] Miss["Delegate: (env y)"] end
Query["Query variable y"] --> Check Check -->|"#t (match)"| Hit Check -->|"#f (no match)"| Miss Miss --> OuterEnv["Outer Environment"] ```
- Higher-order representation: procedures in the host language directly model procedures and lexical environments in the guest language. - Symbol comparison in the environment is handled by `eq?`. - If `(eq? y x)` succeeds, `arg` is returned; otherwise, fallback evaluation delegates to `(env y)`.
[51:34](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=3094s) Testing the Interpreter and the Empty Environment
When Byrd tests `eval-expr` on an expression like `add1`, the initial run fails because the starting environment was omitted from the top-level invocation. Acknowledging a clever catch from the room, he defines what the initial empty environment must look like: yet another `lambda` of `y`. When a variable lookup reaches this base environment without finding a match, it invokes Scheme's built-in `error` procedure to signal an unbound variable error.
With the empty environment wired in, basic evaluations return the expected numbers, and probing an unbound identifier like `x` cleanly throws the unbound error. Byrd demonstrates evaluating an identity function `((lambda (x) x) 5)`, which yields `5`. He then verifies higher-order behavior by applying an identity-returning `lambda` to itself and feeding that result `5`. To pave the way for real recursive computation without adding complex built-in forms, Byrd adds a simple `zero?` primitive clause to the interpreter's dispatch table.
- The base empty environment is modeled as `(lambda (y) (error 'lookup "Unbound variable"))`. - Successfully evaluated primitive applications and higher-order expressions like `(((lambda (x) x) (lambda (x) x)) 5)`. - Added a `zero?` clause to test numerical base cases in recursive programs.
[53:57](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=3237s) Running Factorial via the Poor Man's Y Combinator
To definitively prove that the interpreter works, Byrd introduces the ultimate test: calculating factorial without built-in recursive constructs. The interpreter lacks `define` and mutual recursion forms like `letrec`. Byrd explains that this limitation is irrelevant because “Lambda and the answer everything is Lambda as we know.” Recursion can be achieved purely through Dan Friedman's formulation of the “Poor Man's Y combinator.”
Byrd displays the factorial expression written entirely using nested `lambda` abstractions to compute a fixed point, combined with church-like function passing and arithmetic primitives. Testing the expression directly inside Scheme confirms that the combinator logic is sound. He then feeds the entire expression into the handwritten `eval-expr` interpreter with the empty environment. The interpreter successfully computes the factorial to enthusiastic applause from the room.
- The test program computes factorial without using `define`, `let`, or `letrec`. - Recursion is driven purely by self-application and fixed-point finding via the Poor Man's Y combinator. - The interpreter successfully evaluates the entire recursive combinator term using only `lambda` application and basic primitives.
[55:08](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=3308s) The 3x5 Card Interpreter and Expressive Power
Having verified the interpreter's correctness, Byrd strips away all the auxiliary primitives, labeling everything except the core lambda calculus mechanics as unnecessary clutter: “everything except the last three lines is an Abomination so we are going to remove the Abominable code.” Deleting numbers, arithmetic operators, and the conditional leaves just three fundamental clauses: variable lookup, `lambda` abstraction, and procedure application. Byrd refers to this stripped-down core as the classic "3x5 card interpreter" (or at most a 4x6 card) that holds the entire essence of universal computation.
He explains that numbers can be synthesized using Church encoding, and boolean conditionals can likewise be encoded purely as functions. While concrete data types enhance speed and convenience, the tiny residual core is completely Turing-complete and predates the Turing machine itself. Byrd identifies this system as a call-by-value lambda calculus interpreter with environment passing. He outlines how one can extend this core by introducing a mutable store alongside environments to model operations like Scheme's `set!`, or by applying a Continuation-Passing Style (CPS) transformation to expose first-class continuations and arbitrary control structures, including delimited continuations.
```mermaid flowchart LR accTitle: Core Call-by-Value Lambda Calculus accDescr: The three core evaluation paths of the minimal interpreter.
Expr["Expression"] --> Var["Variable Lookup"] Expr --> Lam["Lambda Abstraction"] Expr --> App["Application (rator rand)"]
Var -->|"Lookup in env"| RetVal["Value"] Lam -->|"Closure over env"| RetProc["Procedure"] App -->|"Eval & Apply"| RetVal ```
- Dan Friedman's exercise for students: “take a 4x6 card stack of them and spend 45 minutes writing this down over and over again till your eyes start bleeding sear it into your brain and your heart and you'll never forget it.” - Church encoding eliminates the mathematical requirement for hardcoded integers or boolean `if` forms. - Adding a store to environment passing supports side effects and mutation (`set!`). - Performing a single CPS transformation yields first-class continuations; double-CPS yields delimited continuations. - Dan Friedman and Mitchell Wand’s textbook *Essentials of Programming Languages* is highlighted as the definitive reference for exploring these architectural variations.
[59:38](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=3578s) John McCarthy's Micro-Manual and the Meaning of Equality
Transitioning to a broader historical perspective, Byrd introduces John McCarthy’s landmark paper, *A micro-manual for LISP - not the whole truth*. In this paper, McCarthy established the foundational rules of Lisp evaluation using an evaluator function named `value`, which directly mirrors Byrd’s own `eval-expr`. Byrd points out McCarthy's explicit rules for primitives like `cons` and his use of standard equal signs to declare that an expression evaluates to a specific value.
Byrd cautions that viewing the equal sign as a simple evaluation arrow misses immense mathematical depth. While compiler writers routinely interpret the equal sign unidirectionally—taking an expression on the left and evaluating it to a value on the right—equality itself is profoundly complex. Byrd argues that “anyone who thinks they understand equality as just kind of some throwaway idea hasn't thought about it hard enough,” pointing to active foundational research like homotopy type theory. When that unidirectional assumption is broken, entirely new models of computation become possible.
- Reference paper: John McCarthy's *A micro-manual for LISP - not the whole truth*. - McCarthy's evaluator function `value` serves the identical semantic role as `eval-expr`. - Most programming language tools treat evaluation as an asymmetric, left-to-right reduction from expression to value. - Mathematical equality is bidirectional, suggesting that evaluation equations can be run in reverse to deduce expressions from values.
[01:01:02](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=3662s) Taking the Equals Sign Seriously: Relational Interpreters and miniKanren
William Byrd challenges the conventional view of programming language evaluation: ordinary interpreters treat the equals sign not as a true mathematical equality, but merely as a one-way, right-hand-side evaluation arrow. Taking the equals sign seriously transforms computation into high school algebra, where unknown variables like $x$, $y$, and $z$ are introduced into equations and solved so that both sides balance. Byrd explains that he, Dan Friedman, and several collaborators spent the last twelve years doing that heavy lifting, starting at Indiana University and continuing at the University of Utah with colleagues like Michael Ballantyne and Greg Rosenblatt. They constructed an interpreter inside miniKanren, an embedded domain-specific language for logic and relational programming.
```mermaid flowchart LR accTitle: Operational Semantics to Relational Interpreter accDescr: Mapping between formal operational semantics and miniKanren relational interpreter logic.
subgraph Semantics["Operational Semantics"] A["Formal Math Rules (3 lines)"] end
subgraph Relational["miniKanren Implementation"] B["eval-expr-o (43 lines)"] end
subgraph Execution["Bi-Directional Query"] C["eval-o Expr Val"] end
A -->|"direct structural translation"| B B -->|"solve for unknowns"| C ```
Rather than being an unwieldy artifact, the entire relational interpreter fits in roughly 43 lines of Scheme. When comparing the mathematical operational semantics side-by-side with the miniKanren implementation (`eval-expo`), the relational code is roughly the same length as the three-line mathematical specification. Byrd jokes about the mundane friction of practical programming while loading the files into his REPL: “You know, it's all fine and good until you have to do things like load a file, right? There's no math for that, probably a monad or something junk for that.” The resulting system allows users to express constraints between an input expression and an output value, solving for whichever components are left unspecified.
- The relational interpreter is implemented in miniKanren, developed by Byrd, Dan Friedman, and collaborators. - Collaborators highlighted include Michael Ballantyne and Greg Rosenblatt at the University of Utah. - The core relational interpreter spans roughly 43 lines of code and matches standard operational semantics line-for-line. - Unlike traditional one-way functions, `eval-expo` treats the relationship between expression and value symmetrically.
[01:04:37](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=3877s) Forward Evaluation and Backward Program Synthesis
Byrd begins by querying `eval-o` in the standard forward direction using logic variables: passing the constant expression `5` with an unknown query variable `x` yields `5`. Passing an identity procedure `(lambda (x) x)` returns a closure data structure, and applying that closure to `5` evaluates successfully to `5`. Primitive constructors such as `cons` also behave as expected, for instance evaluating `(cons 'a '(b))` into `(a b)`. Because it is an interpreter written relationally, it handles ordinary forward execution just like any Lisp evaluation function.
```mermaid flowchart TD accTitle: Forward vs Backward Interpretation accDescr: Showing both evaluation directions supported by the relational interpreter.
subgraph Forward["Forward Evaluation"] In1["Expression: ((lambda (x) x) 5)"] --> EvalF["eval-o"] EvalF --> Out1["Value: 5"] end
subgraph Synthesis["Backward Program Synthesis"] In2["Value: (I want milkshake)"] --> EvalB["eval-o"] EvalB --> Out2["Generated Expressions (e.g. quote, car, thunks)"] end ```
The true expressive power emerges when running the interpreter in reverse: holding the output constant and solving for the input expression. By asserting that the output must equal the list `(I want milkshake)`, miniKanren synthesizes valid Scheme expressions that produce that exact result. Asking for a single answer produces the literal quoted list `'(I want milkshake)`. Requesting additional solutions produces an immediate procedure application (a zero-argument thunk whose body is the quoted list), followed by structural decompositions such as taking the `car` of a list containing `(I want milkshake)`. Byrd connects this to a Valentine’s Day blog post by his Utah lab leader, Matt Might, titled *99 Ways to Say I Love You in Racket*: by running `eval-o` backward for 99 solutions yielding `(I love you)`, the relational engine automatically synthesizes 99 distinct, fully valid Scheme programs that compute that exact value.
- Forward execution: `(eval-o '((lambda (x) x) 5) q)` binds `q` to `5`. - Backward execution: `(eval-o q '(I want milkshake))` generates programs that evaluate to `(I want milkshake)`. - Matt Might's blog post *99 Ways to Say I Love You in Racket* is replicated automatically by running a query for 99 distinct expressions evaluating to `(I love you)`.
[01:09:02](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=4142s) Generating Quines and Relational Program Inversion
Byrd turns to John McCarthy's foundational 1960 paper, pointing out McCarthy's challenge problem: “A difficult mathematical type exercise: find a list $e$—that means a program expression $e$—such that the value of $e$ is equal to $e$. What is that called? A quine.” Instead of manually constructing a self-reproducing program using complicated Gödel-numbering or string-formatting tricks, Byrd simply states the mathematical identity directly in miniKanren: `(eval-o e e)`. The relational engine immediately enumerates trivial quines such as self-evaluating numbers (`5`) and booleans (`#t`, `#f`), before searching deeper and synthesizing non-trivial quines featuring closures and quotes without any side conditions or manual hints.
Building on an idea suggested by Michael Ballantyne, Byrd moves to non-trivial recursive functions, such as an implementation of `append` defined via `letrec`. Running the function normally with inputs `'(a b c)` and `'(d e)` produces `'(a b c d e)`. Because the relation is bi-directional, any sub-expression can be replaced with an unknown logic variable. By fixing the output to `'(a b c d e)` and replacing the first input argument with a logic variable while keeping the second argument as `'(d e)`, the interpreter solves for the missing input and returns `'(a b c)`. Byrd sets up the next demonstration by placing logic variables `x` and `y` simultaneously into both argument slots to infer all possible list partitions that append to the target list.
- McCarthy defined the quine problem in 1960 as finding an expression $e$ where the value of $e$ equals $e$. - Querying `(eval-o e e)` automatically discovers self-evaluating literals (`5`, `#t`, `#f`) and synthesizes higher-order quines. - An interpreter executing a recursive `letrec` definition of `append` can invert computation to infer missing input arguments from a known output list `(a b c d e)`.
[01:12:47](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=4367s) Relational Programming and Equal Signs Without Direction
William Byrd demonstrates the bidirectional flexibility of running an interpreter relationally in miniKanren. Continuing with the list-appending example, he asks the system to find pairs of lists that, when appended together, yield `'(a b c d e)`. The initial query with an empty list paired with `'(a b c d e)` returns immediately as one valid solution. Byrd then switches the query to a `run*`, which exhaustively produces every possible pair of lists that concatenate to `'(a b c d e)`.
```mermaid flowchart LR accTitle: Bidirectional Relational Evaluation accDescr: Diagram showing how a relational interpreter can run forward, backward, or solve for code expressions. subgraph Inputs["Query Modes"] A["Forward: Eval(code, env) → Output"] B["Backward: Solve for Inputs"] C["Synthesis: Solve for Code Expressions"] end Inputs --> D["Relational Scheme Interpreter in miniKanren"] D --> E["Answers / Constraints"] ```
He explains that while logic programming languages like Prolog or miniKanren typically allow these kinds of searches, the programmer here did not actually write a logic program: they simply supplied a standard Scheme function to the relational interpreter. Byrd emphasizes the philosophical and mathematical shift of relational computing:
> “...we’re taking the equal sign seriously we’re saying that equality really does mean equal and there’s no direction to that equal sign we should be able to solve for any side...”
Testing consistency, Byrd evaluates an expression where `'(a b c)` and `'(d e)` are concatenated to produce `'(a b c d e)`. miniKanren responds with constraint gibberish confirming that the statement is entirely consistent. Byrd then asks the audience where else a logic variable could be placed. When an audience member suggests placing it directly in the code, Byrd agrees enthusiastically, targeting an expression inside the code—replacing a variable `s` with a logic variable—and using `run 1` to successfully infer the missing symbol. At this threshold, Byrd notes, the interpreter enters the realm of program synthesis, using the relational semantics of the interpreter to search and solve constraints over inputs, outputs, or code itself.
- Querying with `run*` over two unknown sublists returns all partitions summing to `'(a b c d e)`. - The relational equality has no inherent direction: outputs can determine inputs, or inputs and outputs can determine missing code fragments. - Relational program synthesis searches an exponential state space using the relational operational semantics of Scheme.
---
[01:15:08](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=4508s) The "Genie" Problem: Synthesizing Code from Tests
Byrd experiments with replacing the entire body of the recursive call—specifically the function name `append`—with a logic variable. When he asks miniKanren to synthesize the operator using `run 1`, the audience laughs at the system's cheeky solution: rather than recreating `append`, miniKanren outputs a procedure that takes an argument, ignores it, and simply hard-codes the literal list `'(a b c d e)`. Byrd remarks on how literal logic engines are:
> “miniKanren’s like prove me wrong I defy you to prove me wrong.”
To force the engine to actually synthesize the general definition of `append`, Byrd explains that one must treat miniKanren like a genie granting a wish: you must constrain it with multiple distinct test cases so it cannot cheat by returning a constant value. He constructs an expression containing two separate calls—one appending empty lists to produce an empty list, and another appending non-empty lists. With those constraints in place, miniKanren produces the intended `append` procedure. Byrd then queries for two answers (`run 2`) to see what alternative miniKanren invents; it immediately yields a variadic lambda application with zero arguments that returns `append`, demonstrating another technically correct loophole.
Acknowledging that the naive interpreter is far too slow for complex synthesis tasks, Byrd introduces Barliman, a specialized tool he built alongside Greg Rosenblatt and Michael Ballantyne. Byrd built the graphical frontend in Swift, backed by an optimized relational evaluation engine that runs approximately 10 million times faster than the naive miniKanren implementation.
---
[01:18:16](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=4696s) Live Synthesis in Barliman Using Gensyms
Launching Barliman, Byrd shows a template definition of `append` in the upper-left editor pane where the function name, parameter list, and body are replaced by logic variables (e.g., `,a`). In the test window, he supplies input–output pairs to drive the synthesis.
| Test Attempt | Inputs Provided | Expected Output | Synthesized Function Behavior | |:--- |:--- |:--- |:--- | | **Attempt 1** | `'()` and `'()` | `'()` | Variadic function ignoring inputs, returning `'()` | | **Attempt 2** | `'(a)` and `'(b)` | `'(a b)` | Checks if 2nd arg is null; returns `'()` if null, else returns literal `'(a b)` | | **Attempt 3** | `gensym` inputs | Combined `gensym` output | Correct recursive list traversal using `cons`, `car`, and `cdr` |
To stop the synthesizer from hard-coding output symbols like `'a` and `'b`, Byrd introduces generated symbols (`gensym`). Because generated symbols are guaranteed not to appear literally within the generated code, the synthesizer can no longer pass tests by hard-coding returned constants. After adding a test case with a list of length two using gensyms, Barliman works for a moment before generating a fully formed recursive `append` using `null?`, `car`, `cdr`, and `cons`. Byrd then uses the editor pane to replace the raw synthesized parameter placeholders with human-readable names (`l` and `s`), demonstrating fully automated, example-based program synthesis.
---
[01:21:12](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=4872s) Test-Driven Development and Continuous Proof Engines
Byrd pivots to another paradigm: interactive test-driven development (TDD). If a desired program is too complex for Barliman to synthesize fully from scratch, the developer can begin writing the code manually while leaving holes for difficult sub-expressions.
```mermaid flowchart TD accTitle: Barliman Continuous Verification Flow accDescr: User edits code with holes while background miniKanren worker proves consistency or reports counterexamples against test cases. A["Developer writes partial code (with holes)"] --> B["Barliman Background Engine"] C["Test Suite (I/O examples)"] --> B B --> D{"Consistent?"} D -- "Yes" --> E["Synthesize remaining holes / Show valid solution"] D -- "No" --> F["'Computer says no': Highlight incompatible test case"] ```
Byrd demonstrates this by specifying test cases and intentionally giving the function an incorrect name or invalid structure. The interface reacts instantly:
> “...if I call it like you know concat miniKanren’s like or he like no wrong computer says no fail...”
When Byrd inputs the wrong variable into an arm of a conditional branch, Barliman flags that the partial program has become provably inconsistent with the test cases. Byrd highlights the power of having a relational theorem prover running continuously behind an editor:
> “...it’s constantly in the background taking up all your cores and heating your apartment and desperately trying to prove and this is a proof because miniKanren is a theorem prover that the partially specified code you’ve written is actually incompatible with one of your tests.”
He observes that an inconsistency does not necessarily mean the test is wrong—it could mean the partial code is incorrect, the tests are faulty, or even that the interpreter semantics are buggy, but they are undeniably incompatible. Byrd concludes that constraint-driven relational program synthesis is finally maturing to the point of becoming practical for real-world development workflows.
[01:24:20](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=5060s) Relational Semantics, miniKanren, and the Beauty of the Interpreter
Program synthesis is experiencing a modern renaissance, but Byrd emphasizes that the particular approach he is showcasing is unique: it takes the original, elegant Lisp interpreter and treats the mathematical equal signs in its definition with complete seriousness. While relational semantics is well understood conceptually in mathematical formalism, Byrd and his collaborators created the relational logic programming language miniKanren to directly encode those relations and execute them bidirectionally. By coupling relational interpreters with deep search optimizations, the system transforms program execution into a "fill-in-the-blank game" capable of generating code from inputs and outputs.
```mermaid flowchart TD accTitle: Relational Interpreter Capabilities accDescr: Diagram illustrating how a single relational interpreter enables evaluation, program synthesis, and theorem proving. subgraph Core ["Relational Core"] RI["Relational Interpreter<br>(eval-expr-o)"] end subgraph Modes ["Execution Modes"] EVAL["Forward: Evaluation<br>(Expression → Value)"] SYNTH["Backward: Program Synthesis<br>(Input/Output Pairs → Expression)"] PROOF["Dual: Proof Checking → Theorem Proving"] end Core --> EVAL Core --> SYNTH Core --> PROOF ```
This flexibility extends beyond simple code synthesis; for example, turning a standard proof checker into a relation immediately yields an automated theorem prover. Byrd points attendees interested in deeper examples to the talk he gave with Greg Rosenblatt at Clojure/conj, as well as to resources at `minikanren.org`. He extends an open invitation to anyone wanting to contribute to the research, improve the user interface of Barliman, or simply learn more, offering weekend intro and advanced Google Hangout sessions: “send me an email drop my line hang out we'll get a milkshake and we can talk about these things if this thing you're interested in you want to actually do some research work on it hacking whatever you could probably improve the user interface of Barliman if nothing else”.
Bringing the talk back to its central premise, Byrd reflects on why a higher-order Lisp interpreter written in Lisp is the most beautiful piece of code ever created. It represents a remarkably dense amalgam of foundational ideas that reveals different profound insights depending on how it is approached—whether viewed through the infinite metacircular tower of 3-Lisp and Black, through program transformation, or through relational semantics. Byrd marvels at how such an ancient, well-understood artifact continues to surprise: “in some sense we know everything about the interpreter but in some other real sense like I would not have guessed that you could do this sort of thing just by taking that program and encoding it in a logic programming language but you can turns out you know I think that's really amazing”. He concludes by urging the audience to study it deeply and discover their own perspectives on its mechanics, met with enthusiastic applause.
- **Resources & Community**: - Website: `minikanren.org` - Talk reference: Clojure/conj presentation co-presented with Greg Rosenblatt detailing relational synthesis examples. - Community: Weekly weekend Google Hangouts (introductory and advanced tracks) hosted by William Byrd. - Project UI: Barliman, an interactive smart editor backed by miniKanren synthesis.
---
[01:27:36](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=5256s) Q&A: Meta-Constraints, Algorithmic Complexity, and Stylistic Heuristics
Opening the floor to questions, an audience member asks whether one can apply meta-level constraints to the program being synthesized—such as specifying a constant-time algorithm ($O(1)$) or bounded memory consumption ($O(N)$), rather than just relying on input/output examples as constraints.
Byrd acknowledges that specification expressiveness is a central challenge in program synthesis. Beyond test cases, relational systems can synthesize code via relational type inferencers or high-level logical properties. However, constraining by computational complexity or runtime guarantees is not yet supported in their engine, though it remains a clear goal: “we're not at the point where we can do things like like talk about the desired run time although it is something I'd like to be able to do”. He notes that other researchers within the broader program synthesis community are actively tackling execution-cost constraints.
```mermaid flowchart LR accTitle: Structural Priors in Synthesis accDescr: Pipeline showing how simple syntax priors prune the relational search space. DEF["define"] -->|"statistically expects"| LAM["lambda"] LAM -->|"statistically expects"| COND["if / cond"] COND -->|"order-of-magnitude prune"| FAST["Accelerated Relational Search"] ```
Byrd shares an experimental breakthrough he began hacking on just the day before: biasing the relational search space with structural human coding habits. By examining parent-child syntactic relationships in standard Scheme—observing that a `define` almost always encloses a `lambda`, and a `lambda` typically encloses a conditional `if` or `cond`—he specialized the relational interpreter with simple stylistic priors. Rather than solely relying on language semantics, injecting basic knowledge of human programming style improved synthesis speeds by multiple orders of magnitude. The current research effort focuses on composing these stylistic priors with existing relational pruning optimizations.
---
[01:29:55](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=5395s) Q&A: Search Spaces and Environment Scoping in Relational Interpreters
A second audience member asks about the earlier live demo synthesizing `append`: how did the system actually know to look for `append`, and how did the recursive function name enter the search space in the first place?
Byrd clarifies that the relational engine did not magically guess `append` on its very first attempt—earlier iterations simply produced trivial expressions, such as returning one of the literal argument lists. The reason `append` became available to the search space is rooted entirely in the semantics of lexical scoping and variable lookup implemented within the interpreter itself. Because the target synthesis problem was wrapped inside a `letrec` block defining `append`, the semantics of `letrec` dictates that the function's own name is bound inside the lexical environment of its body.
When the relational interpreter explores potential terms to synthesize an expression, one of its fundamental branches is variable lookup against the active environment. Because `append` was present in the binding environment alongside the formal parameters, the engine automatically evaluated recursive calls to `append` as valid candidates to satisfy the constraints. Byrd closes the session by thanking the audience.
## Notable quotes > [08:30](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=510s) Just like CPS is the gateway drug of program transformations lisp in lisp is the gateway drug of the whole world of programming languages I think. — William Byrd
> [59:38](https://www.youtube.com/watch?v=OyfBQmvr2Hc&t=3578s) anyone who thinks they understand equality as just kind of some throwaway idea hasn't thought about it hard enough — William Byrd
## Mentioned 3-Lisp, A micro-manual for LISP - not the whole truth, Alan Kay, Alan Turing, Alonzo Church, Andrew Plotkin (zarf), Barliman, Black (programming language), Chartbeat, Chez Scheme, Clojure/conj, Dan Friedman, Daniel J. Edwards, David S. Wise, DSSSL, Essentials of Programming Languages, Greg Rosenblatt, Haskell, Henry Baker, Indiana University, James Clerk Maxwell, Java, JavaScript, John McCarthy, Kenichi Asai, Kurt Gödel, LISP 1.5 Programmer's Manual, Lists and Lists, Matt Might, Matthias Felleisen, Michael Ballantyne, Michael I. Levin, Michael Nielsen, miniKanren, Mitchell Wand, Nada Amin, Paul Erdős, Paul W. Abrahams, pmatch, Prolog, R4RS Scheme, Racket, Scheme, SGML, Strange Loop, Structure and Interpretation of Computer Programs (SICP), Swift, The Little LISPer, The Little Schemer, The Reasoned Schemer, The Seasoned Schemer, Timothy P. Hart, Two Sigma, University of Utah, William E. Byrd, XML
## Video description Meetup: http://bit.ly/2okyAH3 Audio: http://bit.ly/2qkAczZ
Sponsored and hosted by Two Sigma (@twosigma) and Chartbeat (@Chartbeat)
Description ------------------ William E. Byrd "explores what he considers to be the most beautiful program ever written---a Lisp interpreter written in Lisp---and a few of the many amazing ideas related to this metacircular interpreter."
References
- The Little Schemer by Daniel P. Friedman and Matthias Felleisen - Essentials of Programming Languages by Daniel P. Friedman and Mitchell Wand - John McCarthy's Recursive functions of symbolic expressions and their computation by machine, Part I - LISP 1.5 Programmer's Manual by John McCarthy, Paul W. Abrahams, Daniel J. Edwards, Timothy P. Hart and Michael I. Levin (see especially page 13!) - John McCarthy's A micro-manual for LISP - not the whole truth - "Maxwell's equations of software" examined by Ken Shirriff - Lisp as the Maxwell’s equations of software by Michael Nielsen - miniKanren, live and untagged: quine generation via relational interpreters (programming pearl) by William E. Byrd, Eric Holk, and Daniel P. Friedman - The Reflective Language Black by Asai, Kenichi - Programming Should Eat Itself by Nada Amin, Strange Loop, 2014
Bio ----- William E. Byrd (@webyrd) is a Research Assistant Professor in the School of Computing at the University of Utah. He is co-author of 'The Reasoned Schemer', and is co-designer of the miniKanren relational programming language. He loves StarCraft (BW & SC2). Ask him about the scanning tunneling microscope (STM) he is building.
[Source](https://www.youtube.com/watch?v=OyfBQmvr2Hc)