1 / 15

Generics

Generics. "It is easy to study the rules of overloading and of templates without noticing that together they are one of the keys to elegant and efficient type-safe containers.“ -Bjarne Stroustrup. Polymorphism (1).

rgillum
Download Presentation

Generics

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Generics "It is easy to study the rules of overloading and of templates without noticing that together they are one of the keys to elegant and efficient type-safe containers.“ -Bjarne Stroustrup

  2. Polymorphism (1) • Because LISP doesn’t use type checking, we can easily write useful general functions that work for several different data types >(defun zip (xs ys) (if (and (consp xs) (consp ys)) (cons (cons (car xs) (car ys)) (zip (cdr xs) (cdr ys))) nil)) ZIP >(zip '(a b c d) '(1 2 3 4)) ((A . 1) (B . 2) (C . 3) (D . 4))

  3. Polymorphism (2) • But what if we want a function to behave differently depending on the types of its arguments? (defstruct square (length 0)) (defstruct circle (radius 0)) (defun perimeter (shape) (cond ((square-p shape) (* 4 (square-length shape))) ((circle-p shape) (* 2 PI (circle-radius shape))) (t 0))) • We have to have a case for every possible shape • For any shape we add, we have to update the perimeter function

  4. A Better Way: Generics • Generics allow the same function name to be associated with several different types (a kind of overloading) (defgeneric perimeter (shape)) (defmethod perimeter ((shape square)) (* 4 (square-length shape))) (defmethod perimeter ((shape circle)) (* 2 PI (circle-radius shape))) >(perimeter (make-square :length 5)) 20 >(perimeter (make-circle :radius 2)) 12.566370614359172954L0 • Generics also form the basis of LISP’s Object System (OOP) • Not supported in GCL yet. Try CLISP

  5. Adding Methods • Now it is easy to expand the functionality of the perimeter method. • If we add a new shape… (defstruct rectangle (width 0) (height 0)) • We simply add a new method (defmethod perimeter ((shape rectangle)) (+ (* 2 (rectangle-width shape)) (* 2 (rectangle-height shape))) )

  6. Extra Parameters • The type declarations following the variable name is called a specializer • Methods can take more than one parameter, optionally specializing on any of them (a method that doesn’t specialize on any parameter is basically a function) • type-of and typep are useful for determining type (be aware of supertypes) >(typep 5 'number) T > (type-of 5) (INTEGER 0 16777215)

  7. Extra Parameters Example (defgeneric combine (a b)) (defmethod combine ((a number) (b number)) (+ a b)) (defmethod combine ((a character) (b character)) (code-char (+ (char-code a) (char-code b)))) (defmethod combine (a (b cons)) (cons a b)) >(combine 5 6) 11 >(combine #\A #\B) #\U0083 >(combine 'A '(5 B S 30)) (A 5 B S 30) >(combine 3 'A) Error

  8. Benefits • Finally, some well structured type safety! • Methods throw an error if no definition fits the types • Extra expressive power: allows for polymorphism even when the underlying algorithms are different for different data types • Takes advantage of inheritance (as we’ll see in a moment. But first…)

  9. While We’re Talking About Types • Structures can also require that their slots have particular types, providing even more type safety >(defstruct rectangle (width 0 :type number) (height 0 :type number)) RECTANGLE >(make-rectangle :width 5.6 :height 3) #S(RECTANGLE :WIDTH 5.6 :HEIGHT 3) >(make-rectangle :width 'A :height '(3 4 5)) Error • Now, on to dealing with inheritance…

  10. Call-Next-Method • If we want a method to be mostly the same for all members of a class, but slightly specialized for certain subclasses, then we can split up the code across different methods • Done with call-next-method. It calls the next method that fits the arguments in decreasing order of specificity • Similar to calling methods of a super class in Java

  11. Call-Next-Method Example (1) • Suppose we want to have several types of bank accounts • For all accounts, a withdraw consists of checking for available funds and removing them if possible • Savings accounts are only allowed 3 withdraws per month, or else there is an additional service charge of $3 for each withdraw • Checking accounts are sometimes linked to a savings account to provide overdraft protection. If a withdraw exceeds available funds in a checking account, then the difference is taken from the backup savings account • First design the structures (defstruct account (balance 0)) (defstruct (savings (:include account)) (withdraws 0)) (defstruct (checking (:include account)) (backup nil))

  12. Call-Next-Method Example (2) • Now design the methods (defgeneric withdraw (source amount)) (defmethod withdraw ((source account) (amount number)) (let ((b (account-balance source))) (if (> amount b) (error "Insufficient funds") (progn (setf (account-balance source) (- b amount)) source)))) (defmethod withdraw ((source savings) (amount number)) (setf (savings-withdraws source) (1+ (savings-withdraws source))) (when (> (savings-withdraws source) 3) (setf amount (+ 3 amount)) ) (call-next-method source amount)) (defmethod withdraw ((source checking) (amount number)) (let ((overdraft (- amount (account-balance source)))) (when (and (plusp overdraft) (checking-backup source)) (withdraw (checking-backup source) overdraft) (setf (account-balance source) (+ (account-balance source) overdraft))) (call-next-method)))

  13. Call-Next-Method Example (3) >(defvar s (make-savings :balance 1000 :withdraws 3)) S >(withdraw s 500) #S(SAVINGS :BALANCE 497 :WITHDRAWS 4) >(defvar c (make-checking :balance 10 :backup s)) C >(withdraw c 20) #S(CHECKING :BALANCE 0 :BACKUP #S(SAVINGS :BALANCE 484 :WITHDRAWS 5)) > (withdraw c 500) Insufficient funds error

  14. Another Specializer • Methods can also be specialized based on equality with a specific value (defvar exception (make-savings :balance 1000 :WITHDRAWS 5)) (defmethod withdraw ((source (eql exception)) (amount number)) (setf (savings-withdraws source) 0) (call-next-method)) >(withdraw exception 200) #S(SAVINGS :BALANCE 800 :WITHDRAWS 1)

  15. What Else Can Generics Do? • Although we’ve been using generics with structures, they were actually designed to work with the Common LISP Object System (CLOS) • LISP has types called classes which are defined with defclass • Classes aren’t too different from structures, though they are created (make-instance) and accessed (slot-value) differently • Perhaps the most interesting feature of classes is that they allow for multiple inheritance, which is not so scary in LISP since methods are defined separately from the classes • http://gigamonkeys.com/book/object-reorientation-classes.html can provide more information for those interested

More Related