From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Johan Andersson Newsgroups: gmane.emacs.help Subject: Re: How to describe something in Lisp? Date: Thu, 5 Feb 2009 08:22:27 +0100 Message-ID: <929ccd880902042322y52fafa17yf54b11bb6f510dc8@mail.gmail.com> References: <929ccd880902030623t1ccb3567weecce35deb378f73@mail.gmail.com> <7c63jq3319.fsf@pbourguignon.anevia.com> <87ljslipmt.fsf@galatea.local> NNTP-Posting-Host: lo.gmane.org Mime-Version: 1.0 Content-Type: multipart/alternative; boundary=0015174c3ea43aa0dc046226c49b X-Trace: ger.gmane.org 1233818605 5753 80.91.229.12 (5 Feb 2009 07:23:25 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Thu, 5 Feb 2009 07:23:25 +0000 (UTC) Cc: help-gnu-emacs@gnu.org To: "Pascal J. Bourguignon" Original-X-From: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Thu Feb 05 08:24:39 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 1LUybB-00084F-DI for geh-help-gnu-emacs@m.gmane.org; Thu, 05 Feb 2009 08:24:37 +0100 Original-Received: from localhost ([127.0.0.1]:59256 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1LUyZs-0005cG-CO for geh-help-gnu-emacs@m.gmane.org; Thu, 05 Feb 2009 02:23:12 -0500 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1LUyZD-0005R8-SD for help-gnu-emacs@gnu.org; Thu, 05 Feb 2009 02:22:31 -0500 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1LUyZC-0005QM-LF for help-gnu-emacs@gnu.org; Thu, 05 Feb 2009 02:22:31 -0500 Original-Received: from [199.232.76.173] (port=58392 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1LUyZC-0005QJ-H3 for help-gnu-emacs@gnu.org; Thu, 05 Feb 2009 02:22:30 -0500 Original-Received: from nf-out-0910.google.com ([64.233.182.191]:31400) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1LUyZB-0007wV-Qm for help-gnu-emacs@gnu.org; Thu, 05 Feb 2009 02:22:30 -0500 Original-Received: by nf-out-0910.google.com with SMTP id c7so14982nfi.26 for ; Wed, 04 Feb 2009 23:22:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:received:in-reply-to:references :date:message-id:subject:from:to:cc:content-type; bh=bnrJXUfVeUOcsWfFrX4OSe9QiisfixP9LScaKccm3/g=; b=rgLO6UZtJPEt2AbZ6te6xGq6ecW95Ez9mtiA7HRSwg9dCVwR4czUj3bU5wV+RmSMlw zj/QTdBdZmwLpPEa4WM3HU93jhpMUQXRzR2odoEp98VTtvNhT6Hwaftn1lGxw0chUyC3 cnPYrKzxErFsFtbxJAB8hJ/XzoHjAD1C9ZCZ4= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; b=eGbxSXHmc6KvgMX3DY7eEbk0W8xKZDKQr8nuTLx7pSmaJCMDZfha+sKepIEYxPyWVD 3m4x/2MS5oLDRVFtSxMQAHWK9+OmLETFAMnx5dTXk8FSysi/vcUq7y+lZSbEHy/cY/IU Ecr1+JKMWEVDbh/BL5wowG13DcY+5GVteUyqo= Original-Received: by 10.210.38.17 with SMTP id l17mr152278ebl.51.1233818547298; Wed, 04 Feb 2009 23:22:27 -0800 (PST) In-Reply-To: <87ljslipmt.fsf@galatea.local> X-detected-operating-system: by monty-python.gnu.org: GNU/Linux 2.6 (newer, 2) 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:61913 Archived-At: --0015174c3ea43aa0dc046226c49b Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Easier to read now, thanks! For the mode I was asking about, regular p-lists are fine. But I'm planning to do another mode. In this mode I will have a general class and one subclass for each programming language, where each subclass defines what should be done in that particular language. After your description I think defclass will work best since there's inheritance involved. On Thu, Feb 5, 2009 at 3:28 AM, Pascal J. Bourguignon wrote: > > 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__ > --0015174c3ea43aa0dc046226c49b Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Easier to read now, thanks!

For the mode I was asking about, regular= p-lists are fine. But I'm planning to do another mode. In this mode I = will have a general class and one subclass for each programming language, w= here each subclass defines what should be done in that particular language.= After your description I think defclass will work best since there's i= nheritance involved.

On Thu, Feb 5, 2009 at 3:28 AM, Pascal J. Bo= urguignon <pj= b@informatimago.com> wrote:

And now, translated in English:

pjb@informatimago.com (Pascal = J. Bourguignon) writes:

> Tassilo Horn <tassilo@member.fsf.org> writes:
>
>> Johan Andersson <joha= n.rejeep@gmail.com> 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 shoul= d 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 matt= er.  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<= br> 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 d= o 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 (fiel= d)
                    &nbs= p;       (list (make-keyword (symbol-name field))
                    &nbs= p;             field)) fields)))
     ;; readers
     ,@(mapcar (lambda (field)
                  `(defun ,(i= ntern (format "%s-%s" name field)) ; defstruct like naming.
                    &nbs= p;     (object)
                    &nbs= p;  (getf (cdr object) ,(make-keyword (symbol-name field))))) fields)<= br>      ;; writers:
     ,@(mapcar (lambda (field)
                  `(defun ,(i= ntern (format "set-%s-%s" name field)) (object value)
                    &nbs= p;  (assert (not (null object)))
                    &nbs= p;  (setf (getf (cdr object)  ,(make-keyword (symbol-name field))= ) value)))
               fields)
     ,@(mapcar (lambda (field)
                 `(defsetf ,(= intern (format "%s-%s" name field))
                    &nbs= p;     ,(intern (format "set-%s-%s" name field))))
               fields)
    ',name))


(progn (pprint (macroexpand '(define-structure person name birthdate jo= b))) nil)
-->
(progn
 (defun* make-person (&key name birthdate job)
   (list 'person :name name :birthdate birthdate :job job))<= br>  (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&q= uot;)
--> (person :name "Tintin" :birthdate 1918 :job "Reporter= ")

(let ((tintin (make-person :name "Tintin" :birthdate 1918 :job &q= uot;Reporter")))
 (setf (person-birthdate tintin) 1920)
 tintin)
--> (person :name "Tintin" :birthdate 1920 :job "Reporter= ")

(let ((tintin (make-person :name "Tintin" :birthdate 1918 :job &q= uot;Reporter")))
 (insert (format "%s is a %s\n" (person-name tintin) (perso= n-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__

--0015174c3ea43aa0dc046226c49b--