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