Historical Perspective

I Second oldest programming language after Fortran CS345H: Programming Languages I Developed in late 1950’s at MIT by John McCarthy

Lecture: LISP I Original purpose: to facilitate research in artificial intelligence

I In research, one may not initially know solution to problem; Thomas Dillig thus, development of program and research hand-in-hand

I Thus, program may need to be changed radically as a result of experimental evaluation: exploratory programming

I LISP is popular for exploratory programming

Thomas Dillig, CS345H: Programming Languages Lecture: LISP 1/30 Thomas Dillig, CS345H: Programming Languages Lecture: LISP 2/30

LISP, Lists, and Dialects Overview Of Important Ideas in LISP

I The main data structures in LISP are lists I Functional, expression-based language

I LISP is named after its main data structure: LISt Processor I Heavily influenced by lambda calculus

I Another feature of LISP: need to write lots of parentheses! I First language to allow recursive and higher-order functions

I Students who hate parentheses in LISP have given it many nicknames: I Lists are fundamental data type

I ”Lost In Stupid Parentheses” I ”Programs as data:” programs and data represented same way

I ”Lots of Irritating Superfluous Parentheses” I Dynamic scoping (in original LISP)

I Today, there are many variations of the original LISP: I First language that has garbage collection , Scheme,

Thomas Dillig, CS345H: Programming Languages Lecture: LISP 3/30 Thomas Dillig, CS345H: Programming Languages Lecture: LISP 4/30

LISP as a Functional Language LISP and Lambda Calculus

I Just like L and λ-calculus, Lisp also has lambda expressions! I LISP is a language I This is how you write a function that adds 1 to its argument: I Characteristic of functional languages (lambda (x) (+ x 1)) 1. No state or mutable data I In LISP, must write + x 1 instead of x+1 because all 2. No statements (such as assignment), only expressions! operations are in prefix form (to make parsing easier!)

3. Expressions have no side-effects I This is how you write application expressions in LISP: ((lambda (x) (+ x 1)) 3) I You are already very familiar with a functional language:L!

I Evaluates to 4 I All functional languages have their roots in lambda calculus

I Don’t forget any of the parentheses – otherwise, won’t work!

Thomas Dillig, CS345H: Programming Languages Lecture: LISP 5/30 Thomas Dillig, CS345H: Programming Languages Lecture: LISP 6/30

1 Higher-Order Functions Named vs. Anonymous Functions

I Just like L and λ-calculus, very easy to write higher-order I Lambda expressions such as (lambda (x) (+ x 1)) are functions in LISP anonymous functions (i.e., functions without a name)

I A higher-order function is one that takes other functions as I Like L, LISP also allows giving functions a name input or returns a function

I This is how you name functions in LISP: I Here is higher-order function in LISP for composing two functions: (define compose (lambda (f g) ((lambda (x) (f(g x))))) (lambda (f g) ( (lambda (x) (f(g x))))) I You can call named functions like this: I This function takes as input two function f and g and another (compose (lambda (x) (+x x)) (lambda (x) (* x x))) argument x and computes f(g(x))

Thomas Dillig, CS345H: Programming Languages Lecture: LISP 7/30 Thomas Dillig, CS345H: Programming Languages Lecture: LISP 8/30

Named Functions and Recursion Named Functions and Recursion

(define f (lambda (x) I As we saw in L, named functions make recursion more natural (cond ((eq x 0) 0) (true + x f(- x 1))))

I Here is a recursive function in LISP: (define f (lambda (x) I The expression (eq e1 e2) evaluates to true if e1 and e2 (cond ((eq x 0) 0) (true + x f(- x 1)))) are equal, and to false otherwise

I So, what does the recursive function above compute? I This function uses a conditional expression: (cond (p1 e1) ... (pn en)) I Recall: possible to encode recursion without named functions

I Here, we evaluate each condition p in order from left-to-right I McCarthy’s original paper on LISP remarks that lambda expressions are not sufficient for writing recursive functions I If pi is the first expression that evaluates to true, then we evaluate corresponding expression ei I Apparently, Y-combinator not known to McCarthy and his group back then – you know better!

Thomas Dillig, CS345H: Programming Languages Lecture: LISP 9/30 Thomas Dillig, CS345H: Programming Languages Lecture: LISP 10/30

Atoms and Lists List Operators

I Two fundamental data types in LISP: atoms and lists I List operations in LISP are: car cdr I Atoms are integers, floating point numbers and symbolic values, such as x I cons appends two lists or atoms together

I Lists in LISP are singly-linked lists; each cell in list called cons I car,cdr take lists apart: car gives first elem,cdr gives rest

I A cons consists of two pointers, called car and cdr I nil denotes empty list

I Can think of car as data stored in current list cell and cdr is I Example: (cons 1 2 (cons 3 4)) evaluates to (1 2 (3 4)) a pointer to the rest of the list I Example:(cons 1 2 (cdr (cons 3 4))) evaluates to (1 2 3)

Thomas Dillig, CS345H: Programming Languages Lecture: LISP 11/30 Thomas Dillig, CS345H: Programming Languages Lecture: LISP 12/30

2 Programs as Data Dynamic vs. Static Scoping

I In LISP, programs and data have the same internal representation. I The original version of LISP was dynamically scoped I Can write LISP programs that build data structures representing other LISP programs and evaluate them! I Modern LISP dialects are statically scoped (e.g., Scheme)

I There is a built-in eval function that calls the Lisp interpreter I Statically scoped languages use the textually closest definition from the Lisp interpreter of a variable (source-code based)

I Example: Suppose we have read function that reads input I Dynamically scoped languages use the most recent run-time from user definition of a variable (run-time notion)

I (eval (read)) executes LISP program source code entered by user!

Thomas Dillig, CS345H: Programming Languages Lecture: LISP 13/30 Thomas Dillig, CS345H: Programming Languages Lecture: LISP 14/30

Dynamic vs. Static Scoping Example Garbage Collection

I Consider the following program: I LISP is the first programming language that had the idea of (define h (lambda (a) (+ a 1))) garbage collection (define g (lambda (z) ( + (h 0) z)) (define f (lambda (y) ( I A memory location is garbage if it can no longer be accessed (define h (lambda (a) (+ a 2))) by the program (g y)))) I If a memory location is garbage, changing the value stored in (f 3) that location cannot change the result of the program

I In statically-scoped dialect of LISP, what does this program I Thus, if we had a way of finding all memory locations that are evaluate to? 4 garbage, we could reclaim those memory locations and use them for some other purpose I In dynamically-scoped LISP, what does this program evaluate to? 5

Thomas Dillig, CS345H: Programming Languages Lecture: LISP 15/30 Thomas Dillig, CS345H: Programming Languages Lecture: LISP 16/30

Explicit vs. Automatic Memory Management Advantages of Garbage Collection

I Garbage collection is the task of detecting garbage during the I Garbage collection makes programming much easier! execution of a program and reclaiming this unused memory I In explicitly-memory managed languages, programmer must I Operating system keeps a list of available memory called the track which memory locations are unused free list I Explicit memory management constant source of headaches: I In languages that are not garbage-collected (e.g., C)

programmer must explicitly call free to deallocate an unused I Memory leaks: forgetting to delete unused memory) memory location I Dangling pointers: deleting memory that’s still in use I In garbage-collected languages like LISP and Java, the programmer does not need to worry about deallocating I In automatically memory-managed languages like LISP and memory – this is done automatically by the run-time system Java, such errors are not possible!

Thomas Dillig, CS345H: Programming Languages Lecture: LISP 17/30 Thomas Dillig, CS345H: Programming Languages Lecture: LISP 18/30

3 Disadvantages of Garbage Collection How Does Garbage Collection Work?

I In past few decades, lots of research on garbage collection I Potential disadvantage of garbage collection is overhead I Original LISP garbage collector used simple algorithm called I In automatically garbage-collected languages, we must do Mark-and-Sweep work at run-time to figure out what is garbage I In Mark-and-Sweep, we reserve a bit for each memory cell, I Identifying what is garbage and reclaiming it takes time; thus, called tag bit, to identify whether it is accessible there is a performance penalty I If tag bit is 1, means cell is in use; if it is 0, it’s unused I While the garbage collector is running, program can feel sluggish, unresponsive I Mark-and-Sweep has two phases, called mark and sweep

I But for many programmers, this is a small price to pay for I In mark phase, garbage collector sets tag bits inconvenience of manually managing memory I In sweep phase, garbage collector reclaims cells with tag bit 0

Thomas Dillig, CS345H: Programming Languages Lecture: LISP 19/30 Thomas Dillig, CS345H: Programming Languages Lecture: LISP 20/30

Mark-and-Sweep Stop the World Collectors

I Mark-and-sweep is easy to understand, easy to implement I When memory is low, garbage collector initializes tag bits of all memory cells to 0 I But it stops the world: while garbage collector is running, nothing else can happen I Then, identifies all memory locations directly used in the program, called root nodes I As a result, program is unresponsive while collector is running

I Starting from root nodes, we follow all links (i.e., pointers) I People have come up with much more sophisticated garbage and change tag bit of each location we accessed to 1 collection algorithms

I This is all there is to the mark phase I Concurrent garbage collectors run concurrently with the program – they don’t “stop the world” I In the sweep phase, we check tag bit of each memory cell

I But much more difficult to implement and get right – cells I If tag bit of that cell is 0, we add that cell to the free list can become reachable while collector is running!

Thomas Dillig, CS345H: Programming Languages Lecture: LISP 21/30 Thomas Dillig, CS345H: Programming Languages Lecture: LISP 22/30

LISP and Garbage Collection Pure vs. Impure LISP

I The part of LISP we talked about so far is a pure functional I LISP was first automatically garbage-collected language language

I This idea has been extremely influential I Pure functional language means expressions do not have side effects I Almost any modern language invented today is automatically memory managed I However, for performance/efficiency reasons, it might be desirable to modify data I However, garbage collection does not eliminate all headaches I For example, suppose you need to change value of one I It is still possible to leak memory element in a list

I If you leave references (i.e. pointers) to memory cells that I In pure LISP, only way to do this is to create a new list that is program won’t use, you can still leak memory identical to old one except for one cell

I But this can be very inefficient

Thomas Dillig, CS345H: Programming Languages Lecture: LISP 23/30 Thomas Dillig, CS345H: Programming Languages Lecture: LISP 24/30

4 Impure Features of LISP Quoted Expressions

I Thus, many functional programming languages have an escape hatch to allow modifying data/state

I Another interesting feature of LISP is that programmer has I Even early versions of LISP had some impure expressions the freedom to decide whether an expression should be which can have side effects evaluated or not

I Two notable impure expressions in LISP, rplaca and rplacd I If you quote an expression, written ‘e, this means do not evaluate this expression I (rplaca x y) replaces car field of cons cell x with y

I For example, if you type +1 2, this expression evaluates to 3. I (rplacd x y) replaces cdr field of cons cell x with y

I But if you write ‘(+ 1 2), it evaluates to + 1 2 I Example: rplaca (cons 1 2) 3) yields the list (1 3)

I However, we haven’t allocated any new memory – just modified existing cons cell

Thomas Dillig, CS345H: Programming Languages Lecture: LISP 25/30 Thomas Dillig, CS345H: Programming Languages Lecture: LISP 26/30

Summary of LISP’s Notable Features Influences of LISP

I LISP has had a lot of influence on many functional I Father of all functional programming languages programming languages today

I Supports recursion, higher-order functions, and anonymous functions (lambda expressions) I Design of ML, Haskell, and many other functional languages influenced by LISP

I Programs and data have same representation: can build data structures representing LISP programs at run-time and I However, arguably LISP does not have the nicest syntax – evaluate them parentheses and prefix-notation are quite cumbersome

I Despite this, some wide-used software written in LISP I Introduced idea of garbage collection

I For instance, popular text editor emacs is written in LISP as I All in all, successful and very influential programming language well as Linux graphical toolkit gtk

Thomas Dillig, CS345H: Programming Languages Lecture: LISP 27/30 Thomas Dillig, CS345H: Programming Languages Lecture: LISP 28/30

L and LISP A Funny Quote

I Our course language L is very similar to LISP, but with nicer syntax ”A LISP programmer knows the value of everything, I In L, parantheses are not as cumbersome and allows infix but the cost of nothing” notation

I Also, we also added a static type system and type inference -Alan Perlis

I You’ve already implemented an interpreter for a LISP-like functional language!

Thomas Dillig, CS345H: Programming Languages Lecture: LISP 29/30 Thomas Dillig, CS345H: Programming Languages Lecture: LISP 30/30

5