LISP
A More Elegant Weapon...
What's a LISP?
Text
A family of many
Who's a LISP
- Common Lisp
- Scheme
- Clojure
- Racket
- Emacs
- And more...
What makes LISP special?
- The read-eval-print-loop
- Universally recognizable
- Everything is a list (even LISP)
- S-Expressions
- Truly expressive macros
The read-eval-print-loop (REPL)
(loop (print (eval (read))))
LISP: Universally Recognizable
; Clojure
(defn factorial [x]
(if (< x 2)
1
(* x (factorial (dec x)))))
; Common Lisp
(defun fact (n)
(if (< n 2)
1
(* n (fact(- n 1)))))
; Scheme
(define (factorial n)
(if (<= n 0)
1
(* n (factorial (- n 1)))))
; Racket
(define (factorial n)
(if (= 0 n)
1
(* n (factorial (- n 1)))))
(define (factorial n)
(if (<= n 0)
1
(* n (factorial (- n 1)))))
(defn factorial [x]
(if (< x 2)
1
(* x (factorial (dec x)))))
(defun fact (n)
(if (< n 2)
1
(* n (fact(- n 1)))))
(define (factorial n)
(if (= 0 n)
1
(* n (factorial (- n 1)))))
Factorial in Common Lisp, Scheme, Racket, and Clojure
Lists are LISP...
; Create an empty list
(def a-list ())
(def a-list (cons 5 nil)) ; nil <--> empty list
(def a-list (cons 1 (cons 2 (cons 3 (cons 4 (cons 5 nil))))))
Lisp is, fundamentally, about lists (LISt Processing)
(map inc '(1 2 3 4 5)) ; = (2 3 4 5 6)
(filter even? (map inc '(1 2 3 4 5))) ; = 2 4 6
(reduce + (filter even? (map inc '(1 2 3 4 5)))) ; = 12
; Implementation of Python's range function
(defn range [start finish]
(loop [result nil current (dec finish)]
(if (> current 0)
(recur (cons current result) (dec current))
result)))
; Using range function
(reduce + (filter even? (map inc (range 1 6))))
And list processing...
...and LISP is Lists
(println '(println "Hello world!"))
LISP expressions are lists
Expressions resolve to a value
(+ 1 1); --> 2
Expressions can contain expressions
(+ (* 2 2) (/ 4 2)); --> 6
Expressions can be evaluated
(def x '(reduce * '(1 2 3 4 5))); Our variable contains an expression
(println x); --> (reduce * '(1 2 3 4 5))
(eval x); We can evaluate this expression to retrieve the result (120)
Data is Code, Code is Data
read:
; Reads a line from stdin and parses it into an S-Expression
(def form (read))
eval:
; Evaluates a LISP form
(eval '(println "Hello"))
; Reads, parses, and evaluates a line from stdin
(eval (read))
Revisiting the REPL
(def basic-repl
(loop []
(print (eval (read)))
(recur)))
Code that modifies Code
LISP contains an incredibly powerful macro system
(defn greet-person-f [name)
(println (str "Hello " name "!")))
(defmacro greet-person-m [name]
`(println (str "Hello " ~name "!")))
So what's the difference?
; Call the function
(greet-person-f "ACM")
; Call the macro
(greet-person-m "ACM")
Hello ACM!
nil
Hello ACM!
nil
Wait...what?
Macros, Macros, Macros
The macroexpand function
(macroexpand '(greet-person-f "ACM"))
; --> (greet-person-f "ACM")
(macroexpand '(greet-person-m "ACM"))
; --> (clojure.core/println (clojure.core/str "Hello " "ACM" "!"))
Macros modify the structure of code
Can't C do this too?
C Macros perform textual substitution:
#define greetPerson(name) printf( "Hello %s!\n", name )
LISP macros are expressions evaluated at compile time:
(defmacro const
"Evaluate the constant expression expr at compile time."
[expr]
(eval expr))
LISP macros can create functions (or macros):
(defmacro defconst [constant-name expr]
"Define a compile time constant"
`(defmacro ~constant-name (const ~expr)))
(defmacro defn-
"Create private function"
[name & decls]
(list* `defn (with-meta name (assoc (meta name) :private true)) decls))
Syntactic Convenience
(first (.split (.replace (.toUpperCase "a b c d") "A" "X") " "))
Thread First (->)
(-> "a b c d"
.toUpperCase
(.replace "A" "X")
(.split " ")
first)
Thread Last (->>)
(->> (range)
(map #(* % %))
(filter even?)
(take 10)
(reduce +))
(macroexpand '(->> (range)
(map #(* % %))
(filter even?)
(take 10)
(reduce +)))
(reduce +
(take 10
(filter even?
(map (fn* [p1__1211#] (* p1__1211# p1__1211#))
(range)))))
Fully Featured Macros
Implement new language features (like ADT support):
(defn- emit-constructor [adt-name type-name args]
"Emits a type constructor for a particular instance of an ADT"
(let [type-name# (symbol type-name)]
(if (empty? args)
`(defn ~type-name# [~@args] (with-meta {} {:type ~type-name :adt ~adt-name}))
`(defn ~type-name# [~@args]
(with-meta
(struct (create-struct ~@(map keyword args)) ~@args )
{:type ~type-name :adt ~adt-name})))))
(defmacro defadt [adt-name & constructors]
"Emits the code to create ADT-like behavior, along with a testing function"
(if (every? list? constructors)
`(do
(defn ~(symbol (str adt-name "?")) [~'x]
(= (:adt (meta ~'x)) ~(str adt-name)))
~@(for [[type-name args] constructors]
(emit-constructor (str adt-name) (symbol type-name) args)))))
Fully Featured Macros
Implement new language features (like ADT support):
(defadt Tree
(Empty [])
(Leaf [value])
(Node [left value right]))
(def other-tree (Node (Node (Leaf 1) 2 (Leaf 3)) 4 (Node (Leaf 5) 6 (Empty))))
(defmulti string-tree (fn [tree] (-> tree meta :type)))
(defmethod string-tree nil [value] value)
(defmethod string-tree Empty [_] "nil")
(defmethod string-tree Leaf [leaf] (str (leaf :value)))
(defmethod string-tree Node [node]
(let [left (string-tree (:left node))
value (string-tree (node :value))
right (string-tree (:right node))]
(format "((%s) %s (%s))" left value right)))
(-> other-tree string-tree read-string flatten pprint) ; (1 2 3 4 5 6 nil)
Questions?
LISP - A More Elegant Weapon
By gizmo385
LISP - A More Elegant Weapon
An introduction to LISP programming. Emphasizes how LISP differs from other styles of programming, and how those differences make it powerful.
- 1,788