SlideShare a Scribd company logo
Write Your Own Lisp in Clojure
Learn Lisp by making a Lisp
1
An Interpreter
2
Demo
3
Meta-circular evaluator
4
Eval
5
Apply
(defn form-apply [proc args]
(letfn [(primitive? [p] (= (first p) 'primitive))
(procedure? [p] (= (first p) 'procedure)) ;<-- from lambda
(proc-params [p] (second p))
(proc-body [p] (nth p 2))
(proc-env [p] (nth p 3))]
(cond (primitive? proc) (apply (second proc) args) ;<-- magic
(procedure? proc) (eval-seq (proc-body proc)
(extend-env (proc-env proc)
(proc-params proc)
args)))))
6
Primitive expressions
true, 123, "words" nil
var, my-fn
(defn form-eval [exp env]
(cond (self-evaluating? exp) exp
(symbol? exp) (env-get exp env)
(defn self-evaluating? [x]
(or (number? x)
(string? x)
(nil? x)
(boolean? x)))
7
Enviroment
8
Enviroment
(defn env-find [var env action not-found] ...)
(defn env-get [var env] ...)
(defn env-set [var val env] ...)
(defn extend-env [env] ...)
9
Initial Enviroment
(defn setup-env []
(-> '()
(extend-env (keys primitive-procs)
(map #(list 'primitive %) (vals primitive-procs)))
(extend-env)
(built-in!)
(extend-env)))
(def primitive-procs {'true true
'+ +
'car first
...})
primitive-procs
10
quote
(quote 1) => 1
(quote a) => a
(quote (+ 1 1)) => (+ 1 1)
(defn form-eval [exp env]
(cond (self-evaluating? exp) exp
(symbol? exp) (env-get exp env)
(= (first exp) 'quote) (second exp)) ;;<- here
11
if
(if <cond-expr> expr else-expr)
(defn form-eval [exp env]
(let [exp (macroexpand exp env)]
(cond (self-evaluating? exp) exp
(symbol? exp) (env-get exp env)
(= (first exp) 'quote) (second exp)
(= (first exp) 'if) (eval-if exp env)))) ; <- here
(defn eval-if [exp env]
(let [[a0 a1 a2 a3] exp]
(if (form-eval a1 env)
(form-eval a2 env)
(form-eval a3 env))))
12
Why is "if" a special form?
Can we just write a if function?
(defn iif [condition stat else]
(if (true? condition)
expr
else-expr))
(iif true (+ 1 2) (* 0 0)) ;=> 3
(iif false (+ 1 2) (* 0 0)) ;=> 0
(iif true (+ 1 2) (/ 0 0)) ;=> Error: ArithmeticException Divide by zero
13
begin
(begin expr1 expr2 ...)
like "do" in clojure
(defn form-eval [exp env]
(cond (self-evaluating? exp) exp
(symbol? exp) (env-get exp env)
(= (first exp) 'quote) (second exp)
(= (first exp) 'if) (eval-if exp env)
(= (first exp) 'begin) (eval-seq (rest exp) env))) ; <-- here
(defn eval-seq [exp env]
(reduce #(form-eval %2 env) nil exp))
14
lambda
(lambda (x y) (+ x y))
(lambda () 5)
(defn form-eval [exp env]
(cond (self-evaluating? exp) exp
; ...
(= (first exp) 'lambda) (eval-lambda exp env)))) ;<- here
(defn eval-lambda [exp env]
(list 'procedure ;<-- this is for form-apply
(second exp)
(drop 2 exp)
env))
15
define
(define x 1)
(define (f x y) (+ x y))
(define f (lambda (x y) (+ x y)))
(defn form-eval [exp env]
(let [exp (macroexpand exp env)]
(cond (self-evaluating? exp) exp
; ...
(= (first exp) 'define) (eval-define exp env)))) ;<-- here
16
eval-define
(defn define-var [exp]
(if (seq? (second exp)) ;function?
(first (second exp)) ;it's a function so the var is function name
(second exp)))
(defn define-val [exp env]
(let [var (second exp)
make-lambda #(cons 'lambda (cons %1 %2))]
(if (seq? var) ;function?
(form-eval (make-lambda (rest var) (drop 2 exp)) env)
(form-eval (nth exp 2) env))))
(defn eval-define [exp env]
(let [var (define-var exp)
val (define-val exp env)]
(swap! (first env) assoc var val))) 17
set!
(define x 5)
(set! x 10)
(defn form-eval [exp env]
(cond (self-evaluating? exp) exp
;...
(= (first exp) 'set!) (eval-assignment exp env))) ;<-- here
(defn eval-assignment [exp env]
(let [[op var val] exp]
(env-set var val env)))
18
let*
(let* ((var val) ...) body)
(defn form-eval [exp env]
(cond (self-evaluating? exp) exp
;...
(= (first exp) 'let*) (eval-let* exp env))) ;<--here
(defn eval-let* [exp env]
(let [eenv (extend-env env)
[op vars body] exp]
(doseq [[b e] vars]
(swap! (first eenv) assoc b (form-eval e eenv)))
(form-eval body eenv)))
19
defmacro
(defmacro binding (lambda (args) body))
(defn form-eval [exp env]
(cond (self-evaluating? exp) exp
;...
(= (first exp) 'defmacro) (eval-defmacro exp env))); <-here
(defn eval-defmacro [exp env]
(let [[a0 a1 a2] exp
mbody (with-meta (form-eval a2 env) {:ismacro true})]
(swap! (first env) assoc a1 mbody)
"ok"))
20
macroexpand
(defn form-eval [exp env]
(let [exp (macroexpand exp env)] ;<--here
(cond (self-evaluating? exp) exp
(= (first exp) 'macroexpand) (macroexpand (second exp) env)))) ;<-- here
(defn macroexpand [exp env]
(loop [exp exp]
(if (is-macro-call exp env)
(let [mac (env-get (first exp) env)]
(recur (form-apply mac (rest exp))))
exp)))
21
macroexpand (cont.)
(defn is-macro-call [exp env]
(and (seq? exp)
(symbol? (first exp))
(env-find (first exp)
env
#(:ismacro (meta (get @% (first exp))))
#(identity false))))
22
apply else
(defn form-eval [exp env]
(let [exp (macroexpand exp env)]
(cond (self-evaluating? exp) exp
;...
:else (form-apply (form-eval (first exp) env) ;<-- here
(map #(form-eval % env) (rest exp)))))
23
apply
(defn form-apply [proc args]
(letfn [(primitive? [p] (= (first p) 'primitive))
(procedure? [p] (= (first p) 'procedure)) ;;<-- from lambda
(proc-params [p] (second p))
(proc-body [p] (nth p 2))
(proc-env [p] (nth p 3))]
(cond (primitive? proc) (apply (second proc) args) ;;<-- magic
(procedure? proc) (eval-seq (proc-body proc)
(extend-env (proc-env proc)
(proc-params proc)
args)))))
24
Now you can write your own Lisp
in Clojure
25
Try it
(def env (setup-env))
(form-eval
'(define (fact x)
(if (eq? x 1)
1
(* x (fact (- x 1))))) env) ;=> ok
(form-eval
'(fact 6) env)) ;=> 720
More examples
26
Still some thing
Add your built-in functions
REPL
27
Question?
28

More Related Content

PDF
Unit testing with PHPUnit
PDF
GeoGebra JavaScript CheatSheet
PPTX
javascript function & closure
PPTX
AngularJS Testing
PDF
Go: It's Not Just For Google
DOCX
โครงงาน เครื่องคิดเลข
PDF
Promise is a Promise
PDF
FrontDays #3. Иван Федяев, Эволюция JavaScript. Обзор нововведений ECMAScript 6
Unit testing with PHPUnit
GeoGebra JavaScript CheatSheet
javascript function & closure
AngularJS Testing
Go: It's Not Just For Google
โครงงาน เครื่องคิดเลข
Promise is a Promise
FrontDays #3. Иван Федяев, Эволюция JavaScript. Обзор нововведений ECMAScript 6

What's hot (20)

PDF
Unit test
PDF
The Ring programming language version 1.5.2 book - Part 75 of 181
PDF
The Ring programming language version 1.10 book - Part 94 of 212
PDF
Solving callback hell with good old function composition
PDF
Jscex: Write Sexy JavaScript (中文)
PPTX
Reverse Engineering: C++ "for" operator
PPS
Ecma script 5
PDF
Hidden Gems of Ruby 1.9
PDF
The Ring programming language version 1.9 book - Part 91 of 210
KEY
Google Guava
PDF
Коварный code type ITGM #9
PDF
CROCHET - Checkpoint Rollback in JVM (ECOOP 2018)
DOCX
DataStructures notes
PDF
2013-02-21 - .NET UG Rhein-Neckar: JavaScript Best Practices
PDF
Pratt Parser in Python
DOCX
Quinto Punto Parte B
PDF
Functional programming with php7
PDF
Implementing virtual machines in go & c 2018 redux
PDF
PHP for Python Developers
PDF
Fine-grained Processing of CVS Archives with APFEL
Unit test
The Ring programming language version 1.5.2 book - Part 75 of 181
The Ring programming language version 1.10 book - Part 94 of 212
Solving callback hell with good old function composition
Jscex: Write Sexy JavaScript (中文)
Reverse Engineering: C++ "for" operator
Ecma script 5
Hidden Gems of Ruby 1.9
The Ring programming language version 1.9 book - Part 91 of 210
Google Guava
Коварный code type ITGM #9
CROCHET - Checkpoint Rollback in JVM (ECOOP 2018)
DataStructures notes
2013-02-21 - .NET UG Rhein-Neckar: JavaScript Best Practices
Pratt Parser in Python
Quinto Punto Parte B
Functional programming with php7
Implementing virtual machines in go & c 2018 redux
PHP for Python Developers
Fine-grained Processing of CVS Archives with APFEL
Ad

Similar to Lisp (20)

PDF
The Magnificent Seven
PDF
Monadologie
PDF
Next Level Testing
PDF
Introduction To Lisp
PPTX
CQL 实现
PPT
SDC - Einführung in Scala
PDF
Feel of Kotlin (Berlin JUG 16 Apr 2015)
PDF
Parametricity - #cljsyd - May, 2015
PDF
The Functional Programming Triad of Folding, Scanning and Iteration - a first...
PDF
Scala for Jedi
PDF
Introduction to Scala
PDF
A swift introduction to Swift
PDF
Pooya Khaloo Presentation on IWMC 2015
PDF
深入浅出Jscex
PDF
Programming in lua STRING AND ARRAY
PPTX
Groovy puzzlers по русски с Joker 2014
PDF
Are we ready to Go?
PDF
Implement the following sorting algorithms Bubble Sort Insertion S.pdf
PDF
Functional Programming with Groovy
PDF
Damn Fine CoffeeScript
The Magnificent Seven
Monadologie
Next Level Testing
Introduction To Lisp
CQL 实现
SDC - Einführung in Scala
Feel of Kotlin (Berlin JUG 16 Apr 2015)
Parametricity - #cljsyd - May, 2015
The Functional Programming Triad of Folding, Scanning and Iteration - a first...
Scala for Jedi
Introduction to Scala
A swift introduction to Swift
Pooya Khaloo Presentation on IWMC 2015
深入浅出Jscex
Programming in lua STRING AND ARRAY
Groovy puzzlers по русски с Joker 2014
Are we ready to Go?
Implement the following sorting algorithms Bubble Sort Insertion S.pdf
Functional Programming with Groovy
Damn Fine CoffeeScript
Ad

Recently uploaded (20)

PPT
Introduction Database Management System for Course Database
PDF
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
PDF
Adobe Illustrator 28.6 Crack My Vision of Vector Design
PPTX
Transform Your Business with a Software ERP System
PDF
System and Network Administration Chapter 2
PDF
Understanding Forklifts - TECH EHS Solution
PDF
PTS Company Brochure 2025 (1).pdf.......
PDF
top salesforce developer skills in 2025.pdf
PPTX
history of c programming in notes for students .pptx
PPTX
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
PDF
Design an Analysis of Algorithms II-SECS-1021-03
PDF
Wondershare Filmora 15 Crack With Activation Key [2025
PPTX
Online Work Permit System for Fast Permit Processing
PDF
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
PPTX
ManageIQ - Sprint 268 Review - Slide Deck
PDF
Nekopoi APK 2025 free lastest update
PDF
System and Network Administraation Chapter 3
PDF
AI in Product Development-omnex systems
Introduction Database Management System for Course Database
Flood Susceptibility Mapping Using Image-Based 2D-CNN Deep Learnin. Overview ...
Adobe Illustrator 28.6 Crack My Vision of Vector Design
Transform Your Business with a Software ERP System
System and Network Administration Chapter 2
Understanding Forklifts - TECH EHS Solution
PTS Company Brochure 2025 (1).pdf.......
top salesforce developer skills in 2025.pdf
history of c programming in notes for students .pptx
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
Design an Analysis of Algorithms II-SECS-1021-03
Wondershare Filmora 15 Crack With Activation Key [2025
Online Work Permit System for Fast Permit Processing
Audit Checklist Design Aligning with ISO, IATF, and Industry Standards — Omne...
ManageIQ - Sprint 268 Review - Slide Deck
Nekopoi APK 2025 free lastest update
System and Network Administraation Chapter 3
AI in Product Development-omnex systems

Lisp

  • 1. Write Your Own Lisp in Clojure Learn Lisp by making a Lisp 1
  • 6. Apply (defn form-apply [proc args] (letfn [(primitive? [p] (= (first p) 'primitive)) (procedure? [p] (= (first p) 'procedure)) ;<-- from lambda (proc-params [p] (second p)) (proc-body [p] (nth p 2)) (proc-env [p] (nth p 3))] (cond (primitive? proc) (apply (second proc) args) ;<-- magic (procedure? proc) (eval-seq (proc-body proc) (extend-env (proc-env proc) (proc-params proc) args))))) 6
  • 7. Primitive expressions true, 123, "words" nil var, my-fn (defn form-eval [exp env] (cond (self-evaluating? exp) exp (symbol? exp) (env-get exp env) (defn self-evaluating? [x] (or (number? x) (string? x) (nil? x) (boolean? x))) 7
  • 9. Enviroment (defn env-find [var env action not-found] ...) (defn env-get [var env] ...) (defn env-set [var val env] ...) (defn extend-env [env] ...) 9
  • 10. Initial Enviroment (defn setup-env [] (-> '() (extend-env (keys primitive-procs) (map #(list 'primitive %) (vals primitive-procs))) (extend-env) (built-in!) (extend-env))) (def primitive-procs {'true true '+ + 'car first ...}) primitive-procs 10
  • 11. quote (quote 1) => 1 (quote a) => a (quote (+ 1 1)) => (+ 1 1) (defn form-eval [exp env] (cond (self-evaluating? exp) exp (symbol? exp) (env-get exp env) (= (first exp) 'quote) (second exp)) ;;<- here 11
  • 12. if (if <cond-expr> expr else-expr) (defn form-eval [exp env] (let [exp (macroexpand exp env)] (cond (self-evaluating? exp) exp (symbol? exp) (env-get exp env) (= (first exp) 'quote) (second exp) (= (first exp) 'if) (eval-if exp env)))) ; <- here (defn eval-if [exp env] (let [[a0 a1 a2 a3] exp] (if (form-eval a1 env) (form-eval a2 env) (form-eval a3 env)))) 12
  • 13. Why is "if" a special form? Can we just write a if function? (defn iif [condition stat else] (if (true? condition) expr else-expr)) (iif true (+ 1 2) (* 0 0)) ;=> 3 (iif false (+ 1 2) (* 0 0)) ;=> 0 (iif true (+ 1 2) (/ 0 0)) ;=> Error: ArithmeticException Divide by zero 13
  • 14. begin (begin expr1 expr2 ...) like "do" in clojure (defn form-eval [exp env] (cond (self-evaluating? exp) exp (symbol? exp) (env-get exp env) (= (first exp) 'quote) (second exp) (= (first exp) 'if) (eval-if exp env) (= (first exp) 'begin) (eval-seq (rest exp) env))) ; <-- here (defn eval-seq [exp env] (reduce #(form-eval %2 env) nil exp)) 14
  • 15. lambda (lambda (x y) (+ x y)) (lambda () 5) (defn form-eval [exp env] (cond (self-evaluating? exp) exp ; ... (= (first exp) 'lambda) (eval-lambda exp env)))) ;<- here (defn eval-lambda [exp env] (list 'procedure ;<-- this is for form-apply (second exp) (drop 2 exp) env)) 15
  • 16. define (define x 1) (define (f x y) (+ x y)) (define f (lambda (x y) (+ x y))) (defn form-eval [exp env] (let [exp (macroexpand exp env)] (cond (self-evaluating? exp) exp ; ... (= (first exp) 'define) (eval-define exp env)))) ;<-- here 16
  • 17. eval-define (defn define-var [exp] (if (seq? (second exp)) ;function? (first (second exp)) ;it's a function so the var is function name (second exp))) (defn define-val [exp env] (let [var (second exp) make-lambda #(cons 'lambda (cons %1 %2))] (if (seq? var) ;function? (form-eval (make-lambda (rest var) (drop 2 exp)) env) (form-eval (nth exp 2) env)))) (defn eval-define [exp env] (let [var (define-var exp) val (define-val exp env)] (swap! (first env) assoc var val))) 17
  • 18. set! (define x 5) (set! x 10) (defn form-eval [exp env] (cond (self-evaluating? exp) exp ;... (= (first exp) 'set!) (eval-assignment exp env))) ;<-- here (defn eval-assignment [exp env] (let [[op var val] exp] (env-set var val env))) 18
  • 19. let* (let* ((var val) ...) body) (defn form-eval [exp env] (cond (self-evaluating? exp) exp ;... (= (first exp) 'let*) (eval-let* exp env))) ;<--here (defn eval-let* [exp env] (let [eenv (extend-env env) [op vars body] exp] (doseq [[b e] vars] (swap! (first eenv) assoc b (form-eval e eenv))) (form-eval body eenv))) 19
  • 20. defmacro (defmacro binding (lambda (args) body)) (defn form-eval [exp env] (cond (self-evaluating? exp) exp ;... (= (first exp) 'defmacro) (eval-defmacro exp env))); <-here (defn eval-defmacro [exp env] (let [[a0 a1 a2] exp mbody (with-meta (form-eval a2 env) {:ismacro true})] (swap! (first env) assoc a1 mbody) "ok")) 20
  • 21. macroexpand (defn form-eval [exp env] (let [exp (macroexpand exp env)] ;<--here (cond (self-evaluating? exp) exp (= (first exp) 'macroexpand) (macroexpand (second exp) env)))) ;<-- here (defn macroexpand [exp env] (loop [exp exp] (if (is-macro-call exp env) (let [mac (env-get (first exp) env)] (recur (form-apply mac (rest exp)))) exp))) 21
  • 22. macroexpand (cont.) (defn is-macro-call [exp env] (and (seq? exp) (symbol? (first exp)) (env-find (first exp) env #(:ismacro (meta (get @% (first exp)))) #(identity false)))) 22
  • 23. apply else (defn form-eval [exp env] (let [exp (macroexpand exp env)] (cond (self-evaluating? exp) exp ;... :else (form-apply (form-eval (first exp) env) ;<-- here (map #(form-eval % env) (rest exp))))) 23
  • 24. apply (defn form-apply [proc args] (letfn [(primitive? [p] (= (first p) 'primitive)) (procedure? [p] (= (first p) 'procedure)) ;;<-- from lambda (proc-params [p] (second p)) (proc-body [p] (nth p 2)) (proc-env [p] (nth p 3))] (cond (primitive? proc) (apply (second proc) args) ;;<-- magic (procedure? proc) (eval-seq (proc-body proc) (extend-env (proc-env proc) (proc-params proc) args))))) 24
  • 25. Now you can write your own Lisp in Clojure 25
  • 26. Try it (def env (setup-env)) (form-eval '(define (fact x) (if (eq? x 1) 1 (* x (fact (- x 1))))) env) ;=> ok (form-eval '(fact 6) env)) ;=> 720 More examples 26
  • 27. Still some thing Add your built-in functions REPL 27