* A Lisp Newbie's Experience - Why Lisp? - Why now? - I still don't know what I'm doing * Picking a Runtime - Linux: lots of choices - Windows: SBCL, CLISP, a few for-pay (LispWorks) - I've mostly been using SBCL, seems to work best with SLIME on Windows * Weird/Annoying Things for Windows Programmers - Pathnames - The last OS to get support - asdf-install doesn't really work - Hunchentoot single-threaded * Development Environment - Lisp interpreter - emacs (lisp-mode) - Eclipse addin called Cusp - SLIME * Evaluation - Lisp expressions called "forms". - Forms can be atoms (such as numbers) or lists - Lists evaluated by interpreting the first element as a function and the rest as arguments - Quoting prevents evaluation 1 => 1 "foo" => "foo" (+ 1 2) => 3 (make-instance 'foo) => an instance of the foo class (1 2 3) => Error - 1 is not a function (quote (1 2 3)) => (1 2 3) '(1 2 3) => (1 2 3) ** Numbers - Large integers (really large) (expt 2 256) => 115792089237316195423570985008687907853269984665640564039457584007913129639936 - Rationals (/ 300 39) => 100/13 300/39 => 100/13 - Complex numbers (sqrt -1) => #C(0.0 1.0) (exp #C(0.7 0.7)) => #C(1.5402031 1.2972951) * SLIME - Superior Lisp Interaction Mode for Emacs - De facto standard ** Installing Linux: sudo apt-get install slime Windows: Download and unzip the distro .emacs: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (add-to-list 'load-path "C:/bin/slime/slime-2.0/") ; your SLIME directory (setq inferior-lisp-program "C:/bin/sbcl-1.0.9/sbcl --core C:/bin/sbcl-1.0.9/sbcl.core" ; your Lisp system slime-complete-symbol-function 'slime-fuzzy-complete-symbol ; fuzzy symbol completion lisp-indent-function 'common-lisp-indent-function ; How would you like to indent? ) (require 'slime) (slime-setup) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (+ 2 3) ** The REPL M-x slime * Functions (defun foo (a b c d) (+ a b c d)) (foo 1 2 3 4) => 10 (defun bar (&key numerator denominator) (/ numerator denominator)) (bar :denominator 3 :numerator 4) => 3/4 (defun bar2 (&key numerator (denominator 1)) (/ numerator denominator)) (bar2 :numerator 3) => 3 (defun baaz (first &rest rest) (list first rest)) (baaz "a" "b" "c" "d") => ("a" ("b" "c" "d")) (defun quux (a &optional b c) (list a b c)) (quux 1) => (1 nil nil) (quux 1 2) => (1 2 nil) (defun quux2 (a &optional (b nil b-specified) (c 5 c-specified)) (list a b b-specified c c-specified)) (quux2 1 2) => (1 2 t 5 nil) * Object Orientation - Mythbuster: Lisp fully OO - Method dispatch *very* different ** Class definitions ;; person derives from STANDARD-CLASS (defclass person () ()) ;; child derives from person (defclass child (person) ()) ;; hermaphrodite derives from man and woman - order signifcant (defclass hermaphrodite (man woman) ()) (make-instance 'hermaphrodite) ** Slots (defclass person () ((birthplace) (name :reader getname :writer setname :initarg :name :initform "<>") (age :accessor age :initarg :age))) (setf craig (make-instance 'person :age 35)) (slot-value craig 'birthplace) => Error: unbound slot (setf (slot-value craig 'birthplace) "New Ulm, MN") (slot-value craig 'birthplace) => "New Ulm, MN" (getname craig) => "<>" (setname craig "Craig") (getname craig) => "Craig" (age craig) => 35 (age (make-instance 'person)) => Error: slot age is unbound (setf (age craig) 36) (age craig) => 36 ** With-Slots (with-slots (name age) craig (format t "~s is ~s years old" name age)) => "Craig is 35 years old" (with-slots ((his-name name) (his-age age)) craig (format t "~s is ~s years old" his-name his-age)) => "Craig is 35 years old" ** Generic Methods (defgeneric birth-year (entity)) (defmethod birth-year ((person person)) (- 2007 (age person))) (defmethod birth-year ((obj t)) 0) (birth-year craig) (birth-year "foo") (defclass animal () ((whelped :accessor whelped :initarg :whelped) (species :accessor species :initarg :species))) (defmethod birth-year ((animal animal)) (whelped animal)) (birth-year (make-instance 'animal :whelped 2005)) *** Method dispatch (defclass hitter () ()) (defclass hittee () ()) (defclass person (hitter) ()) (defclass stick (hitter) ()) (defclass baseball (hittee) ()) (defclass drum (hittee) ()) (defgeneric hit (hitter hittee) (:documentation "Defines a mechanism for one thing to hit another")) (defmethod hit ((hitter hitter) (hittee hittee)) (format t "Some hitter hit a hittee")) (defmethod hit ((hitter person) (hittee baseball)) "Implementation of a person hitting a baseball" (format t "Crack!")) (defmethod hit ((hitter stick) (hittee drum)) (format t "Boom!")) (defmethod hit ((hitter number) (hittee number)) (format t "The number ~@R is hitting the number ~R" hitter hittee)) (describe 'hit) (hit 2007 172834) (hit (make-instance 'stick) (make-instance 'drum)) (hit (make-instance 'person) (make-instance 'baseball)) (hit "foo" #C(1 7)) (defmethod hit ((hitter t) (hittee t)) (format t "Some ~a hit some ~a" (type-of hitter) (type-of hittee))) (hit "foo" #C(1 7)) *** Call-Next-Method (defclass programmer (person) ()) (defmethod hit ((hitter programmer) (hittee t)) "Implements the way a programmer hits things." (format t "A projectile appears to be approaching rapidly on a parabolic course...") (call-next-method)) (hit (make-instance 'programmer) (make-instance 'baseball)) *** Eql Dispatch (setf *craig* (make-instance 'programmer)) (setf *keith* (make-instance 'programmer)) (defmethod hit ((hitter (eql *craig*)) (hittee t)) (format t "Swish!")) (hit *keith* (make-instance 'baseball)) (hit *craig* (make-instance 'baseball)) *** Before, After, Around (defmethod hit :before ((hitter programmer) (hittee baseball)) (format t "Dropping laptop, picking up bat~%")) (hit *keith* (make-instance 'baseball)) (defmethod hit :after ((hitter (eql *craig*)) (hittee baseball)) (format t "~%I always hated baseball anyway")) (hit *craig* (make-instance 'baseball)) (defmethod hit :around ((hitter programmer) (hittee baseball)) (format t "~%Left elbow up...~%") (call-next-method) (format t "~%Push glasses up on nose~%")) (hit *craig* (make-instance 'baseball)) (hit *keith* (make-instance 'baseball)) *** Constructors (defmethod initialize-instance :after ((hitter hitter) &rest remains) (declare (ignore remains)) (format t "I'm creating a hitter!")) (make-instance 'programmer) * Macros - Programmable compiler ** A (Sort-Of) Real-Life Web Service (defun log-method-start (name) (format t "~%Entering ~a" name)) (defun check-security-headers (xml) (format t "~%Checking security of ~a" xml) (when (string-equal xml "") :error "Shields up, captain!")) (defun log-method-exit (name) (format t "~%Exiting ~a" name)) (defun some-web-service-operation-implementation (data) (format t "~%This is the implementation of some web service operation with data ~a" data)) (defun some-other-web-service-operation-implementation (data) (format t "~%This is the implementation of some other web service operation with data ~a" data)) (defun deserialize (xml) (format nil "~%Deserialized xml ~a" xml)) (defun some-web-service-operation (xml) (log-method-start "some-web-service-operation") (check-security-headers xml) (some-web-service-operation-implementation (deserialize xml)) (log-method-exit "some-web-service-operation")) (defun some-other-web-service-operation (xml) (log-method-start "some-other-web-service-operation") (check-security-headers xml) (some-other-web-service-operation-implementation (deserialize xml)) (log-method-exit "some-other-web-service-operation")) (some-web-service-operation "") (some-other-web-service-operation "") ** Reducing Duplication (defmacro defwebmethod (name implementation) `(defun ,name (xml) (log-method-start (symbol-name ',name)) (check-security-headers xml) (,implementation (deserialize xml)) (log-method-exit (symbol-name ',name)))) (defwebmethod some-web-service-operation some-web-service-operation-implementation) (defwebmethod some-other-web-service-operation some-other-web-service-operation-implementation) (some-web-service-operation "") (some-other-web-service-operation "") (macroexpand-1 '(defwebmethod some-web-service-operation some-web-service-operation-implementation)) (defmacro defwebmethod-2 (name &body implementation) "Defines a web service operation" `(defun ,name (xml) (log-method-start (symbol-name (quote ,name))) (check-security-headers xml) ,@implementation (log-method-exit (symbol-name (quote ,name))))) (defwebmethod-2 do-something (format t "~%blah") (+ 2 3)) (do-something "") (macroexpand-1 '(defwebmethod-2 do-something (format t "~%blah") (+ 2 3))) ; Common Lisp doesn't support hygenic macros (gensym) ** Loop (mapcar #'foo '(1 2 3)) (reduce #'+ '(1 2 3)) (loop for i from 10 to 20 do (format t "~a~%" i)) (loop for c across "foo" collect (char-code c)) (loop for i from 10 until (= 0 (mod i 19)) collect (list i (mod i 17))) * Error Handling - Conditions - Restarts ** Conditions (define-condition failed-authorization (error) ((principal :initarg :principal :reader principal)) (:report (lambda (condition stream) (format stream "Authorization failed for user ~a" (principal condition))))) (defun check-security (xml) (when (string-equal xml "") (error 'failed-authorization :principal "Craig"))) (check-security "") (check-security "") (defun do-something (xml) (handler-case (progn (check-security xml) (format t "Did something with ~a" xml)) (failed-authorization (x) (format t "Authorization failed for ~a" (principal x))))) (do-something "") (do-something "") ** Restarts (defun do-something (xml) (restart-case (progn (check-security xml) (format t "Did something with ~a" xml)) (ignore-error () (format t "Ignored error and did something anyway with ~a" xml)))) (do-something "") (handler-bind ((failed-authorization #'(lambda (c) (invoke-restart 'ignore-error)))) (do-something "")) * .NET Interop - IronScheme (http://codeplax.com/ironscheme) - RDNZL (http://weitz.de/rdnzl/) ** RDNZL - http://weitz.de/rdnzl/ - BSD-style license ;; Note: run emacs cd first to change to RDNZL directory, restart SLIME (load "C:/bin/rdnzl-0.11.0/load.lisp") (in-package :rdnzl) (new "System.Guid") (invoke (new "System.Random") "NextDouble") (property "System.Environment" "CurrentDirectory") (property (property "System.DateTime" "Now") "Year") (setf int-array (list-to-rdnzl-array '(1 2 3) "System.Int32")) (aref* int-array 2) (setf (aref* int-array 2) 19) (aref* int-array 2) *** Errors (defun parse-int (input) (handler-case (invoke "System.Int32" "Parse" input) (rdnzl-error (x) (when (string-equal (property (invoke (rdnzl-error-exception x) "GetType") "FullName") "System.FormatException"))))) (parse-int "3") (parse-int "foo") (parse-int (make-null-object "System.String")) (defun parse-int-better (input) (declare (string input)) (rdnzl-handler-case (invoke "System.Int32" "Parse" input) ("System.FormatException" () -1))) (parse-int-better 3) (parse-int-better "3") (parse-int-better "foo") *** Syntactic Sugar - Need full type names when not in mscorlib (invoke "System.Windows.Forms.MessageBox" "Show" "Message" "Title") ; Fails (invoke "System.Windows.Forms.MessageBox, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" "Show" "Message" "Title") ; Blech (import-assembly "System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") (invoke "System.Windows.Forms.MessageBox" "Show" "Message" "Title") ; Works (use-namespace "System.Windows.Forms") (invoke "MessageBox" "Show" "Message" "Title") (enable-rdnzl-syntax) ;; Can't appear as top-level form (defun show () (let ((user-name [%System.Environment.UserName])) [MessageBox.Show (format nil "Username is : ~a" user-name) "Title"])) (show) (disable-rdnzl-syntax) * XML * Web Development - Hunchentoot ** Hunchentoot - BSD-style license - http://weitz.de/hunchentoot/ * Misc ** Partial Type Hierarchy /data/work/57-DCALTNET/type-hierarchy.png ** Strong Type Checking (defun foo (x y) (declare (float x y)) (+ x y)) (foo "three" 3.0) (foo 2.0 3.0) (defun quux (x y) (the string "some string")) (defun bar () (the float (quux 2.0 3.0))) (bar)