From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: pjb@informatimago.com (Pascal J. Bourguignon) Newsgroups: gmane.emacs.help Subject: Re: How to describe something in Lisp? Date: Thu, 05 Feb 2009 03:28:10 +0100 Organization: Informatimago Message-ID: <87ljslipmt.fsf@galatea.local> References: <929ccd880902030623t1ccb3567weecce35deb378f73@mail.gmail.com> <7c63jq3319.fsf@pbourguignon.anevia.com> NNTP-Posting-Host: lo.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: ger.gmane.org 1233801650 4489 80.91.229.12 (5 Feb 2009 02:40:50 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Thu, 5 Feb 2009 02:40:50 +0000 (UTC) To: help-gnu-emacs@gnu.org Original-X-From: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Thu Feb 05 03:42:05 2009 Return-path: Envelope-to: geh-help-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([199.232.76.165]) by lo.gmane.org with esmtp (Exim 4.50) id 1LUuBn-0000tA-6Q for geh-help-gnu-emacs@m.gmane.org; Thu, 05 Feb 2009 03:42:04 +0100 Original-Received: from localhost ([127.0.0.1]:54822 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1LUuAT-0002sl-S5 for geh-help-gnu-emacs@m.gmane.org; Wed, 04 Feb 2009 21:40:41 -0500 Original-Path: news.stanford.edu!headwall.stanford.edu!news.glorb.com!news2.glorb.com!feeder1-2.proxad.net!proxad.net!feeder1-1.proxad.net!cleanfeed3-a.proxad.net!nnrp3-2.free.fr!not-for-mail Original-Newsgroups: gnu.emacs.help Face: iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAQMAAABtzGvEAAAABlBMVEUAAAD///+l2Z/dAAAA oElEQVR4nK3OsRHCMAwF0O8YQufUNIQRGIAja9CxSA55AxZgFO4coMgYrEDDQZWPIlNAjwq9 033pbOBPtbXuB6PKNBn5gZkhGa86Z4x2wE67O+06WxGD/HCOGR0deY3f9Ijwwt7rNGNf6Oac l/GuZTF1wFGKiYYHKSFAkjIo1b6sCYS1sVmFhhhahKQssRjRT90ITWUk6vvK3RsPGs+M1RuR mV+hO/VvFAAAAABJRU5ErkJggg== X-Accept-Language: fr, es, en X-Disabled: X-No-Archive: no User-Agent: Gnus/5.1008 (Gnus v5.10.8) Emacs/22.3 (darwin) Cancel-Lock: sha1:Y2RlYWYyNjI5MWFiODZlNGFjZTNmZGMzM2E5ZWQ2ZWYyMzcwZTcwYg== Original-Lines: 149 Original-NNTP-Posting-Date: 05 Feb 2009 03:28:12 MET Original-NNTP-Posting-Host: 88.182.134.169 Original-X-Trace: 1233800892 news-2.free.fr 3689 88.182.134.169:57584 Original-X-Complaints-To: abuse@proxad.net Original-Xref: news.stanford.edu gnu.emacs.help:166596 X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Users list for the GNU Emacs text editor List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Original-Sender: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Errors-To: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.help:61908 Archived-At: And now, translated in English: pjb@informatimago.com (Pascal J. Bourguignon) writes: > Tassilo Horn writes: > >> Johan Andersson writes: >> >> Hi Johan, >> >>> Then I could easy update attributes on the objects, remove and add >>> people and then update the file. >>> >>> I tried with a couple of solutions for this in Lisp: >>> >>> 1) One list named people: >>> ( >>> (name age married sex) >>> ... >>> ) >> >> I think a list of plists would be quite intuitive to someone with an OO >> background. Here's a quick and dirty snippet which should get you >> started: >> >> --8<---------------cut here---------------start------------->8--- >> (defvar persons >> '((:name "Klaus" :age 36 :sex male) >> (:name "Claudia" :age 31 :sex female))) >> [...] >> ;; Adjust the ages >> (set-property "Klaus" :age 37) >> (set-property "Claudia" :age 32) Why not. The choice of internal representation doesn't matter. You must be able to change the internal representation at will, depending on the algorithms and specified performance. On the other hand, whether you use defstruct, defclass, a-lists, p-lists (note that defstruct can also generate flat lists and vectors, and in emacs lisp, eieio implements classes as vectors), you must hide this choice behind a functional abstraction. The accessors generated by defstruct or defclass make up this functional abstraction. If you choose another representation, you will have to define yourself the needed functions. There's a little difference between the accessors defined by defstruct and defclass: the formers are normal functions, while the later are methods on generic functions, which allows to apply them on objects of various classes and subclasses. But if you consider only one class, and use the same names in both cases, they'll be exchangeable. In your example, set-property defines a "dynamical" interface on the slots of the object (thep-list) (and in addition encapsulate the person "database"). In some cases it may be a good way to do it; CLOS (and eieio) do define a similar accessor: (slot-value object field). But this is useful more in the case of metaprogramming (eg. a serializer/deserializer) or if there are uniform processings on all the slots. In other cases, I think it's more practical to define specific accessors, as defstruct and defclass do. Of course, if you have a lot of slots (or "classes" of p-lists), it's worthwhile to write a macro to generate them automatically: (require 'cl) (defun make-keyword (name) (intern (format ":%s" name))) (defmacro define-structure (name &rest fields) `(progn (defun* ,(intern (format "make-%s" name)) (&key ,@fields) (list ',name ,@(mapcan (lambda (field) (list (make-keyword (symbol-name field)) field)) fields))) ;; readers ,@(mapcar (lambda (field) `(defun ,(intern (format "%s-%s" name field)) ; defstruct like naming. (object) (getf (cdr object) ,(make-keyword (symbol-name field))))) fields) ;; writers: ,@(mapcar (lambda (field) `(defun ,(intern (format "set-%s-%s" name field)) (object value) (assert (not (null object))) (setf (getf (cdr object) ,(make-keyword (symbol-name field))) value))) fields) ,@(mapcar (lambda (field) `(defsetf ,(intern (format "%s-%s" name field)) ,(intern (format "set-%s-%s" name field)))) fields) ',name)) (progn (pprint (macroexpand '(define-structure person name birthdate job))) nil) --> (progn (defun* make-person (&key name birthdate job) (list 'person :name name :birthdate birthdate :job job)) (defun person-name (object) (getf (cdr object) :name)) (defun person-birthdate (object) (getf (cdr object) :birthdate)) (defun person-job (object) (getf (cdr object) :job)) (defun set-person-name (object value) (assert (not (null object))) (setf (getf (cdr object) :name) value)) (defun set-person-birthdate (object value) (assert (not (null object))) (setf (getf (cdr object) :birthdate) value)) (defun set-person-job (object value) (assert (not (null object))) (setf (getf (cdr object) :job) value)) (defsetf person-name set-person-name) (defsetf person-birthdate set-person-birthdate) (defsetf person-job set-person-job) 'person) (define-structure person name birthdate job) --> person (make-person :name "Tintin" :birthdate 1918 :job "Reporter") --> (person :name "Tintin" :birthdate 1918 :job "Reporter") (let ((tintin (make-person :name "Tintin" :birthdate 1918 :job "Reporter"))) (setf (person-birthdate tintin) 1920) tintin) --> (person :name "Tintin" :birthdate 1920 :job "Reporter") (let ((tintin (make-person :name "Tintin" :birthdate 1918 :job "Reporter"))) (insert (format "%s is a %s\n" (person-name tintin) (person-job tintin)))) Tintin is a Reporter Then, if you notice that p-list are too slow, or that you need subclasses, you can easily substitute defstruct for define-structure, and get structures with direct access slots, or if you notice that you need multiple inheriting, you can substitute a defclass for the define-structure, all with the rest of the program unchanged, since using the functional abstraction defined by these accessors. -- __Pascal Bourguignon__