lisp the ultimate - home - dipartimento di informaticaromani/didattica/ep/lispultimate.pdf · lisp:...
TRANSCRIPT
4/20/2010
1
Lisp: the Ultimate
Programming Language
Giuseppe Attardi
Università di Pisa
Quotes
If it works, it is no longer AI (Artificial
Intelligence)
Marvin Minski
Lisp is executable XML with a friendlier syntax
someone who rediscovered Lisp
I wonder why replacing parenthesis with angle
brackets should make a big difference
Jim Miller
4/20/2010
2
Syntax, syntax
People complained because of Lisp syntax
Once a Japanese programming, when he
heard that I was a Lisp programmer, told me
in admiration:
―Lisp is vely vely difficult: many many parentheses‖
Ironically, even worse syntax has become
popular
Ant: XML madness
Ant is a build system for Java
Ant takes an XML file with specific build
instructions and interprets them by running
specialized Java code
4/20/2010
3
Ant Example
<?xml version="1.0"?>
<project name="Hello" default="compile">
clean" description="remove intermediate files">
<delete dir="classes"/>
</target>
<target name="clobber" depends="clean" description="remove all artifact files">
<delete file="hello.jar"/>
</target>
<target name=" compile" description="compile the Java source code to class files">
<mkdir dir="classes"/>
<javac srcdir="." destdir="classes"/>
</target>
<target name=" jar" depends="compile" description="create a Jar file for the application―>
<jar destfile="hello.jar―>
<fileset dir="classes" includes="**/*.class"/>
<manifest>
<attribute name="Main-Class" value="HelloProgram"/>
</manifest>
</jar>
</target>
</project>
Makefile
clean:
rm –fr classes
clobber: clean
rm hello.jar
classes/%.class: %.java
javac -d classes $<
hello.jar: classes/hello.class
jar –cf hello.jar classes/*.class
4/20/2010
4
Domain Specic Languages
Makefile is an example of a Domain Specific
Language
The mechanisms in Lisp make it suitable to
build DSL
In particular built-in S-expr reader made
simple designing languages with S-expr syntax
Similar to the role of XML readers today
Lisp Contribution to PL
Dynamic Memory Allocation and Garbage
Collection
Dynamic Typing
Higher Order Programming
Closures and Continuations
Object Oriented Programming
Metalevel programming (Macros)
Reflection
4/20/2010
5
Garbage Collection
Lisp:
(cons 'A 'B)
C++:
Symbol* a = new Symbol();
Symbol* b = new Symbol();
Cons* c = new Cons(a, b);
…
delete c; delete b; delete a;
GC: by 1996 it works
GC took 20 years to become mainstream.
In 1993, a group of mathematicians led by prof. Carlo Traverso
decided to switch from Lisp to C++ for the development of a
Computer Algebra System
I was asked to be part of the project, but put the condition to build
a GC for C++
In 1994 I showed to Bill Joy the GC I had developed (CMM) and he
took it to James Gosling who was developing Java
CMM was used as GC in the first release of Java
4/20/2010
6
GC in Java
Java:
String a = "A".intern();
String a = "B".intern();
Pair<String> p = new Pair<String>(a, b);
No delete operator: deallocation is done by the
GC
Topics
Closures
Continuations
Macros
Objects
4/20/2010
7
Getting to Closures
In 1969 Carl Hewitt at MIT introduced Planner, a programming
language for AI based on Procedural Embedding of Knowledge, i.e.
procedural constructs for performing reasoning, such as:
if assert P, assert Q forward chaining
if goal Q, goal P backward chaining
Implementation required complex control structures such as
backtracking
In 1972 Hewitt introduced the Actor Model, as a foundation for
Planner, based on the simple metaphor of message passing
The “spaghetti” stack
In order to handle the complex mechanism
used in problem solving (backtracking,
search, either top-dow/bottom-up or both,
etc.) control structures manipulated the
stack creating a so called ―spaghetti stack‖
A spaghetti stack allowed to save the state of
a computation and restore it later
4/20/2010
8
Actors and Objects
Actors were inspired in part by SIMULA and
Smalltalk: Everything is an actor:
any agent of computation is an actor any data structure is an actor
An actor may have ―acquaintances‖ (other actors it knows)
Actors react to messages sent from other actors
An actor can send messages only to acquaintances and to actors received in messages
Message Passing Metaphor: ―You don’t invoke add with 3 and 2 to get 5;
instead, you send 3 a message asking it to add 2 to itself‖
Simula
Ole Dahl and Kristen Nygaard had the
following intuition when designing Simula 67:
If you take a function activation in Algol 60 and
save it before the function returns, you can resume
it later and that becomes an idle process (object)
The function activation is the constructor of a class
Simula67 provided a Class construct and an
operator (New) for creating instances
They received the 2001 ACM Turing Award
4/20/2010
9
Control Stack
Simula 67: example
Begin
Class Glyph;
Virtual: Procedure print; -- virtual method
Begin … End;
Glyph Class Char(c); -- class inheritance
Character c;
Begin
Procedure print; -- virtual method
OutChar(c);
End;
Glyph Class Line(elements);
Ref (Glyph) Array elements;
Begin
Procedure print; Begin … End; -- virtual method
End;
End;
4/20/2010
10
SmallTalk
Developed by Alan Kay and his team at Xerox
Parc
Designed to allow even young people to
program the Dynabook
A quintessential device for expression and communication, portable and networked
For learning through experimentation, exploration and sharing
Alan Kay received the 2003 ACM Turing Award
Dynabook
Presented for the first time in Pisa in 1975
4/20/2010
11
Objects as Closures
(defun Complex (x y)
(lambda (msg &optional arg)
(case msg
(x x)
(y y)
(set-x (setq x arg))
(set-y (setq y arg))
(add (Complex (+ x (arg 'x)) (+ y (arg 'y))))
)
)
)
4/20/2010
12
Objects (2)
> (setq c1 (Complex 1 2))
> (cl 'x)
1
> (cl 'y)
2
> (setq c2 (c1 'add c1))
> (c2 'y)
4
Closure Implementation
Pair of:
environment (variable bindings)
function
x: 2
y: 3
(lambda (msg &optional arg) …
4/20/2010
13
Meta Interpreter
(defun eval (x env)
(cond ((numberp x) x)
((symbolp x) (lookup x env))
((eq (first x) ‘quote) x)
(t (apply (first x)
(evlis (rest x) env)
env))
)
)
Environments
A list of key-value pairs((N 3) (X (red green blue)))
We say that: name N is bound to 3
name X is bound to the list (red green blue)
If name appears twice, the first is chosen:
(define lookup (x env)
(cond ((null env) NIL)
((eq x (first (first env))) (second (first env)))
(t (lookup x (rest env))))
4/20/2010
14
Apply
(define apply (fn args env)
(cond ((null fn) NIL)
((primitive fn) (primapply fn args env)) ; +, =, …
((symbolp fn)
(apply (lookup fn env) args env))
((eq 'lambda (first fun))
(eval (third fn) (bind (second fn) args env)))
(t (apply (eval fn env) args env)))
)
)
Functional Arguments
(defun map (FUN X)(cond ((null X) X)
(t (cons (FUN (first X))(map FUN (rest X))))
))applies function FUN to each element of the list X
and returns a list of the results
(map '(lambda (x) (list x x)) '(CHITTY BANG))returns
((CHITTY CHITTY) (BANG BANG))
4/20/2010
15
Funarg Problem
(defun consall (X YS)
(map '(lambda (y) (cons X y)) YS))
(consall 'BEAT '(HARVARD YALE))
we expect to get:
((BEAT HARVARD) (BEAT YALE))
we actually get:
((HARVARD YALE) HARVARD) ((YALE) YALE))
Why?
Because
(lambda (y) (cons X y))
is called within map, which binds X to the second argument:(defun map (FUN X)
(cond ((null X) X)(t (cons (FUN (first X))
(map FUN (rest X))))))
which in the first call is(HARVARD YALE)
and in the second is
(YALE)
4/20/2010
16
Solution: closure
Capture the environment with the FUNCTION operator:
(defun consall (X YS)
(map (function (lambda (y) (cons X y)) YS)))
with:
(consall ‘BEAT '(HARVARD YALE))
we actually get:
((BEAT HARVARD) (BEAT YALE))
Fixing eval
(defun eval (x env)
(cond ((numberp x) x)
…
((eq (first x) 'function)
(list 'closure (second x) env))
…
) Create closure
storing the current
environment
4/20/2010
17
Fixing apply:
(define apply (fn args env)
(cond ((null fn) NIL)
…
((eq 'closure (first fun))
(apply (second fn) args (third fn))
…
)
)
Scheme
Dialect of Lisp that adopted lexical scoping
(from Algol 60) and turned all functions into
lexical closures, thereby avoiding to explicitly use
FUNCTION
Lexical scoping allows determining which free
variables are used in a function and hence
avoiding to store the whole environment
4/20/2010
18
Meta Programming
Lisp macros are the most powerful
They provide access to the program code
itself, represented as a list and can
manipulate it with the full language power
(typical macro processors for instance do not allow
recursion)
Macros: extending the language
Function are not enough. Trying defining if:
(defun if (test then else)
(cond (test then)
(t else)))
(if (= x 0) infinity (/ 1 x)) ;; Oops…
4/20/2010
19
Part of the issue on evaluation order
Call by value
Call by name
Call by need
Call by text
Defmacro
(defmacro if (test then else)
(list 'cond (list test then) (list t else)))
A macro is dealt specially by eval: it applies the
macro to the argument expressions, and then it
evaluates the resulting expression.
For example:
(if (= x 0) infinity (/ 1 x)) =>
(cond ((= x 0) infinity) (t (/ 1 x)))
4/20/2010
20
HTML Templates
HTML templates or Web scripting languages
adopt an opposite convention everything is constant except when escaped
HTML
<body>
<h1>Hello {$X}</h1>
</body>
PHP
<body>
<?php echo "Hello “ . $X; ?>
</body>
Backquote Syntax
(defmacro if (test then else)
`(cond (,test ,then) (t ,else)))
(defmacro with-transaction (&body body)
`(let (done) ; watch out: variable capture
(unwind-protect
(prog2 (begin-transaction)
(progn ,@body) ; list splicing
(setq done t))
(if done
(commit-transaction)
(abort-transaction)))))
4/20/2010
21
Classes
(defmacro defclass (name attrs)
`(defun name (,attrs)
(lambda (msg &option arg)
(case msg
,@(map '(lambda (x) (list x x)) attrs)
)
)
)
)
Message Passing
(defmacro send (target msg &rest args)
(funcall ,target ,msg ,@args))
> (defclass pair (x y))
> (setq p (pair 2 3))
> (send p 'x)
2
4/20/2010
22
CLOS
The Common Lisp Object System
Multiple inheritance
Multi-methods or Generic functions:
Can be specialized on any arguments, not just the first
Sophisticated meta-object protocol (MOP),
allowing defining variants of OO models
The flexibility of the CLOS MOP prefigures
aspect-oriented programming
Reflection
Reflection is built in in the language since it uses
dynamic typing
Each object can be queried to know its type and
objects are described by a metaclass, which is a
Lisp object that contains the description of the
class
As we saw, objects and functions can be
generated on the fly and even compiled
4/20/2010
23
Continuations
Normal functions always return to the caller
Hence the control flow is statically determined by
the program code
A simple case where one wants to depart from this
policy is exceptions handling
Scheme introduced an operator for capturing the
current continuation and passing it to a function:
call/cc (call with current continuation)
Example: return
(defun f (cont)
(cont 2)
3)
> (f (lambda (x) x)
3
> (call/cc f) ; current continuation prints result
2
4/20/2010
24
Example: exceptions
(try ‘back body)
(defmacro try (catch . body)(let* ((result NIL)
(old-handler top-handler)(success (call/cc
(lambda (cont)(setq top-handler (cont NIL))(setq result (progn ,@body))T))))
(setq top-handler old-handler)(if success result catch))
(defun throw () (top-handler))
Example: coroutines
Starts a new thread running (proc)
(defun fork (proc)(call/cc
(lambda (k) (enqueue k) (proc))))
Yields the processor to another thread:
(defun yield()(call/cc
(lambda (k) (enqueue k) ((dequeue)))))
4/20/2010
25
Example: generators
(defun tree-iterator (tree)
(let ((caller NIL))
(letf ((next ()
(letrec ((walk (node)
(if (consp node)
(progn
(walk (car node))
(walk (cdr node)))
(call/cc (lambda (state)
(setq next (lambda () (state)))
(caller node))
(walk tree))))
(lambda ()
(call/cc (lambda (cont)
(setq caller cont)
(next)))))
Generator use
Iterating throught the leaves of a tree.
Each call to the generator returns the next value
(setq ti (tree-iter '((1 . 2) . (3 . 4))))
> (ti)
1
> (ti)
2
4/20/2010
26
Scheme and the spaghetti stack
Since continuations break the LIFO pattern
of use of function activations, Scheme had to
address the spaghetti stack problem
The solution was to give up the stack:
function frames are allocated on the heap
and garbage collected
Scheme compiler and runtime
Compilation techniques were studied in order to
relieve the burden on the runtime
In particular compilation itself is based on
translation into continuation passing style, so
functions never return
This allows dealing properly with tail recursion
and reducing the cost of continuations
4/20/2010
27
Common Lisp Implementations
Compile to byte code:
clisp (Haible)
SBCL (CMU)
Compile to C:
ECL (Attardi)
KCL (Yuasa and Hagiya)
GCL (Shelter)
ThinLisp (Allard and Hyde)
Back to Actors
In ―Viewing Control Structures as Patterns of Passing
Messages‖, Hewitt described how to implement control
structures with actors
Steele and Sussman realized that Hewitt actors were
essentially closures that never return but instead invoke a
continuation
(defun fact (n c)(= n 0
(lambda () (c 1))(lambda () (- n 1 (lambda (n-1 c1)
(fact n-1(lambda (nf c2)
(* n nf c))))))
4/20/2010
28
Lambda: the Ultimate Imperative
They had discovered that using closures and
continuations any control structure can be
expressed:
―we realized that the lambda calculus—a small,
simple formalism—could serve as the core of a
powerful and expressive programming language‖
They described how to express many imperative
constructs in the paper
―Lambda: the Ultimate Imperative‖
Dynamic Language
Dynamic language like Lisp provide lots of
flexibility that is helpful in experimenting
language designs or programming patterns
P. Norvig showed that 16 of 23 patterns in the
book Design Patterns by the Gang of Four are
trivial or simpler to implement in Lisp
4/20/2010
29
Lisp Legacy in Current Languages
GC almost everywhere
Closures: Python, JavaScript, C#
Java nested classes, C# delegates are similar to closures
Generators (aka Iterators): Python, C#
List comprehension: Python
Map/Reduce: Python and distributed programming
Meta Programming: some in C++
Reflection: Java, JavaScript, Python, C#
Lisp: it works!
The great ideas of Lisp have proved to work,
hence
they are no longer AI
4/20/2010
30
Lisp Role
Lisp had a significant role in the development of
programming languages
Many of the most significant ideas in
programming were born or experimented with
in Lisp
The flexibility to extend a language in order to
experiment with new programming metaphors
has no equal in any of the current PL
References
G. Steele. A History of Scheme.
http://research.sun.com/projects/plrg/JAOO-
SchemeHistory-2006public.pdf
P. Norvig. Design Patterns in Dynamic Languages.
http://www.norvig.com/design-patterns/
H. Baker. 1995. CONS should not CONS its
arguments, Part II: Cheney on the M.T.A.
http://home.pipeline.com/~hbaker1/CheneyMTA.htm
l