unofficial mirror of help-gnu-emacs@gnu.org
 help / color / mirror / Atom feed
* How to describe something in Lisp?
@ 2009-02-03 14:23 Johan Andersson
  2009-02-03 16:24 ` Thierry Volpiatto
                   ` (2 more replies)
  0 siblings, 3 replies; 14+ messages in thread
From: Johan Andersson @ 2009-02-03 14:23 UTC (permalink / raw)
  To: help-gnu-emacs

[-- Attachment #1: Type: text/plain, Size: 1489 bytes --]

Hi!

As a Java and Ruby programmer I sometimes find it hard to code Lisp. Right
now I'm working on a minor mode for which the structure would obvious for me
in Java or Ruby, but in Lisp is a riddle.

I will not describe the mode itself, but give a description of the problem.
Say I want to store a list of people in a file. And for each person, also
some information on them in the format:
name|age|married|sex

Each time I start the mode, that file should be parsed in to some
datastructure (which kind of is the problem). And on save, the file would be
updated. For me it's obvious to represent a person with a class:
class Person
  var name, age, married, sex

  methods...
end

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)
  ...
)

2) Multiple lists named name, age, married and sex where the index decides
the connection between the lists:
(name1 name2)
(age1 age2)
(married1 married2)
(sex1 sex2)

3) Same as two, but with arrays:
[name1 name2]
[age1 age2]
[married1 married2]
[sex1 sex2]


Each way above has their disadvantages and I think none of them is good
enough. I read something about object orientation in lisp, but I have never
seen this be used in Emacs. So my question is basically: What is the best
way to model something in lisp, that you in an object oriented language
would model with a class.

Thanks!

[-- Attachment #2: Type: text/html, Size: 1654 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: How to describe something in Lisp?
       [not found] <mailman.6644.1233674526.26697.help-gnu-emacs@gnu.org>
@ 2009-02-03 15:48 ` Andreas Politz
  2009-02-03 16:40   ` Pascal J. Bourguignon
  0 siblings, 1 reply; 14+ messages in thread
From: Andreas Politz @ 2009-02-03 15:48 UTC (permalink / raw)
  To: help-gnu-emacs

Johan Andersson wrote:
> Hi!
> 
> As a Java and Ruby programmer I sometimes find it hard to code Lisp. Right
> now I'm working on a minor mode for which the structure would obvious for me
> in Java or Ruby, but in Lisp is a riddle.
> 
> I will not describe the mode itself, but give a description of the problem.
> Say I want to store a list of people in a file. And for each person, also
> some information on them in the format:
> name|age|married|sex
> 
> Each time I start the mode, that file should be parsed in to some
> datastructure (which kind of is the problem). And on save, the file would be
> updated. For me it's obvious to represent a person with a class:
> class Person
>   var name, age, married, sex
> 
>   methods...
> end
> 
> 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)
>   ...
> )
> 
> 2) Multiple lists named name, age, married and sex where the index decides
> the connection between the lists:
> (name1 name2)
> (age1 age2)
> (married1 married2)
> (sex1 sex2)
> 
> 3) Same as two, but with arrays:
> [name1 name2]
> [age1 age2]
> [married1 married2]
> [sex1 sex2]
> 
> 
> Each way above has their disadvantages and I think none of them is good
> enough. I read something about object orientation in lisp, but I have never
> seen this be used in Emacs. So my question is basically: What is the best
> way to model something in lisp, that you in an object oriented language
> would model with a class.
> 
> Thanks!
> 



(defstruct person
   name
   age
   married
   sex)

(person-name (make-person :name "Hans"))

You most likely can read about it here:
(info "(cl)Top")

Other possibilities include association lists
(info "(elisp)Association List Type")
and property lists
(info "(elisp)Property Lists")
.

-ap



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: How to describe something in Lisp?
  2009-02-03 14:23 Johan Andersson
@ 2009-02-03 16:24 ` Thierry Volpiatto
  2009-02-03 16:46 ` Tassilo Horn
       [not found] ` <mailman.6652.1233679633.26697.help-gnu-emacs@gnu.org>
  2 siblings, 0 replies; 14+ messages in thread
From: Thierry Volpiatto @ 2009-02-03 16:24 UTC (permalink / raw)
  To: help-gnu-emacs

Hi,
Johan Andersson <johan.rejeep@gmail.com> writes:

> Hi!
>
> As a Java and Ruby programmer I sometimes find it hard to code Lisp. Right
> now I'm working on a minor mode for which the structure would obvious for me
> in Java or Ruby, but in Lisp is a riddle.
>
> I will not describe the mode itself, but give a description of the problem.
> Say I want to store a list of people in a file. And for each person, also
> some information on them in the format:
> name|age|married|sex
>
> Each time I start the mode, that file should be parsed in to some
> datastructure (which kind of is the problem). And on save, the file would be
> updated. For me it's obvious to represent a person with a class:
> class Person
>   var name, age, married, sex
>
>   methods...
> end
>
> 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)
>   ...
> )
>
> 2) Multiple lists named name, age, married and sex where the index decides
> the connection between the lists:
> (name1 name2)
> (age1 age2)
> (married1 married2)
> (sex1 sex2)
>
> 3) Same as two, but with arrays:
> [name1 name2]
> [age1 age2]
> [married1 married2]
> [sex1 sex2]
>
>
> Each way above has their disadvantages and I think none of them is good
> enough. I read something about object orientation in lisp, but I have never
> seen this be used in Emacs. So my question is basically: What is the best
> way to model something in lisp, that you in an object oriented language
> would model with a class.
>
> Thanks!
Does that help?

,----
| ELISP> (defstruct People name age)
| People
| ELISP> (defvar P (make-People :name "thierry" :age "45"))
| P
| ELISP> (People-name P)
| "thierry"
| ELISP> (People-age P)
| "45"
| ELISP> (setf (People-age P) "46")
| "46"
| ELISP> (People-age P)
| "46"
`----

-- 
A + Thierry Volpiatto
Location: Saint-Cyr-Sur-Mer - France





^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: How to describe something in Lisp?
  2009-02-03 15:48 ` How to describe something in Lisp? Andreas Politz
@ 2009-02-03 16:40   ` Pascal J. Bourguignon
  2009-02-03 16:44     ` Johan Andersson
  0 siblings, 1 reply; 14+ messages in thread
From: Pascal J. Bourguignon @ 2009-02-03 16:40 UTC (permalink / raw)
  To: help-gnu-emacs

Andreas Politz <politza@fh-trier.de> writes:

> Johan Andersson wrote:
>> Hi!
>> As a Java and Ruby programmer I sometimes find it hard to code
>> Lisp. Right
>> now I'm working on a minor mode for which the structure would obvious for me
>> in Java or Ruby, but in Lisp is a riddle.
>> I will not describe the mode itself, but give a description of the
>> problem.
>> Say I want to store a list of people in a file. And for each person, also
>> some information on them in the format:
>> name|age|married|sex
>> Each time I start the mode, that file should be parsed in to some
>> datastructure (which kind of is the problem). And on save, the file would be
>> updated. For me it's obvious to represent a person with a class:
>> [...]
>>  I read something about object orientation in lisp, but I have never
>> seen this be used in Emacs. So my question is basically: What is the best
>> way to model something in lisp, that you in an object oriented language
>> would model with a class.
>> Thanks!
>
> (defstruct person
> [...]

There is also EIEIO which is an implementation of CLOS, the Common
Lisp Object System, adapted for emacs.

You can find it part of http://cedet.sourceforge.net/

Then you can define your objects:

(require 'eieio)

(defclass person
  ((name       :type string :initarg :name      :accessor name)
   (birthdate  :type date   :initarg :birthdate :accessor birthdate))
   (status     :type marital-status :initarg  :martial-status :accessor martial-status)
   (sex        :type (member :male :female) :initarg :sex :accessor sex))

(defmethod age ((p person))
  (date- (now) (birthdate p)))

...

-- 
__Pascal Bourguignon__


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: How to describe something in Lisp?
  2009-02-03 16:40   ` Pascal J. Bourguignon
@ 2009-02-03 16:44     ` Johan Andersson
  2009-02-03 16:54       ` Tassilo Horn
  0 siblings, 1 reply; 14+ messages in thread
From: Johan Andersson @ 2009-02-03 16:44 UTC (permalink / raw)
  To: Pascal J. Bourguignon; +Cc: help-gnu-emacs

[-- Attachment #1: Type: text/plain, Size: 2015 bytes --]

Thanks all for your answers.

All of your examples will work fine in my case. But is it "accepted" by
Emacs users to code a mode using these structures? I've never seen them
before.

On Tue, Feb 3, 2009 at 5:40 PM, Pascal J. Bourguignon <pjb@informatimago.com
> wrote:

> Andreas Politz <politza@fh-trier.de> writes:
>
> > Johan Andersson wrote:
> >> Hi!
> >> As a Java and Ruby programmer I sometimes find it hard to code
> >> Lisp. Right
> >> now I'm working on a minor mode for which the structure would obvious
> for me
> >> in Java or Ruby, but in Lisp is a riddle.
> >> I will not describe the mode itself, but give a description of the
> >> problem.
> >> Say I want to store a list of people in a file. And for each person,
> also
> >> some information on them in the format:
> >> name|age|married|sex
> >> Each time I start the mode, that file should be parsed in to some
> >> datastructure (which kind of is the problem). And on save, the file
> would be
> >> updated. For me it's obvious to represent a person with a class:
> >> [...]
> >>  I read something about object orientation in lisp, but I have never
> >> seen this be used in Emacs. So my question is basically: What is the
> best
> >> way to model something in lisp, that you in an object oriented language
> >> would model with a class.
> >> Thanks!
> >
> > (defstruct person
> > [...]
>
> There is also EIEIO which is an implementation of CLOS, the Common
> Lisp Object System, adapted for emacs.
>
> You can find it part of http://cedet.sourceforge.net/
>
> Then you can define your objects:
>
> (require 'eieio)
>
> (defclass person
>  ((name       :type string :initarg :name      :accessor name)
>   (birthdate  :type date   :initarg :birthdate :accessor birthdate))
>   (status     :type marital-status :initarg  :martial-status :accessor
> martial-status)
>   (sex        :type (member :male :female) :initarg :sex :accessor sex))
>
> (defmethod age ((p person))
>  (date- (now) (birthdate p)))
>
> ...
>
> --
> __Pascal Bourguignon__
>

[-- Attachment #2: Type: text/html, Size: 2874 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: How to describe something in Lisp?
  2009-02-03 14:23 Johan Andersson
  2009-02-03 16:24 ` Thierry Volpiatto
@ 2009-02-03 16:46 ` Tassilo Horn
       [not found] ` <mailman.6652.1233679633.26697.help-gnu-emacs@gnu.org>
  2 siblings, 0 replies; 14+ messages in thread
From: Tassilo Horn @ 2009-02-03 16:46 UTC (permalink / raw)
  To: help-gnu-emacs

Johan Andersson <johan.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 should get you
started:

--8<---------------cut here---------------start------------->8---
(defvar persons
  '((:name "Klaus" :age 36 :sex male)
    (:name "Claudia" :age 31 :sex female)))

(defun print-persons (buffer)
  (set-buffer buffer)
  (dolist (person persons)
    (insert (format "\n;; %s, %s, %s"
                    (plist-get person :name)
                    (plist-get person :age)
                    (plist-get person :sex)))))

(print-persons (current-buffer)) ;; <== C-x C-e here!
;; Klaus, 36, male
;; Claudia, 31, female

(defun set-property (name prop newval)
  (let ((list persons))
    (while (not (string= (plist-get (car list) :name) name))
      (setq list (cdr list)))
    (when (not (null list))
      (let ((plist (car list)))
        (setq persons (remove plist persons))
        (setq persons (cons (plist-put plist prop newval) persons))))))

;; Adjust the ages
(set-property "Klaus" :age 37)
(set-property "Claudia" :age 32)

(print-persons (current-buffer)) ;; <== C-x C-e here!
;; Claudia, 32, female
;; Klaus, 37, male
--8<---------------cut here---------------end--------------->8---

I'm assume that the name (the :name property) is unique, here.

Bye,
Tassilo





^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: How to describe something in Lisp?
  2009-02-03 16:44     ` Johan Andersson
@ 2009-02-03 16:54       ` Tassilo Horn
  2009-02-03 17:07         ` Johan Andersson
  0 siblings, 1 reply; 14+ messages in thread
From: Tassilo Horn @ 2009-02-03 16:54 UTC (permalink / raw)
  To: help-gnu-emacs

Johan Andersson <johan.rejeep@gmail.com> writes:

Hi Johan,

> All of your examples will work fine in my case. But is it "accepted"
> by Emacs users to code a mode using these structures?

`defstruct' is part of GNU Emacs (dunno XEmacs) in its cl library
(Common Lisp features library), so you can expect that users have it.
Code which should be included in stock emacs must not require those
features, though.

EIEIO currently is an external addon, so users would need to install it
before using your mode.

A simple plist approach like the one I've posted doesn't require
anything, but maybe the other structures are a bit more convenient.

So it's your choice to make. ;-)

Bye,
Tassilo





^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: How to describe something in Lisp?
  2009-02-03 16:54       ` Tassilo Horn
@ 2009-02-03 17:07         ` Johan Andersson
  0 siblings, 0 replies; 14+ messages in thread
From: Johan Andersson @ 2009-02-03 17:07 UTC (permalink / raw)
  Cc: help-gnu-emacs

[-- Attachment #1: Type: text/plain, Size: 858 bytes --]

Thanks all, I'll go with the plist example!

On Tue, Feb 3, 2009 at 5:54 PM, Tassilo Horn <tassilo@member.fsf.org> wrote:

> Johan Andersson <johan.rejeep@gmail.com> writes:
>
> Hi Johan,
>
> > All of your examples will work fine in my case. But is it "accepted"
> > by Emacs users to code a mode using these structures?
>
> `defstruct' is part of GNU Emacs (dunno XEmacs) in its cl library
> (Common Lisp features library), so you can expect that users have it.
> Code which should be included in stock emacs must not require those
> features, though.
>
> EIEIO currently is an external addon, so users would need to install it
> before using your mode.
>
> A simple plist approach like the one I've posted doesn't require
> anything, but maybe the other structures are a bit more convenient.
>
> So it's your choice to make. ;-)
>
> Bye,
> Tassilo
>
>
>
>

[-- Attachment #2: Type: text/html, Size: 1344 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: How to describe something in Lisp?
       [not found] ` <mailman.6652.1233679633.26697.help-gnu-emacs@gnu.org>
@ 2009-02-04 10:33   ` Pascal J. Bourguignon
  2009-02-04 11:43     ` Tassilo Horn
  2009-02-05  2:28     ` Pascal J. Bourguignon
       [not found]   ` <7c63jq3319.fsf@pbourguignon.informatimago.com>
  1 sibling, 2 replies; 14+ messages in thread
From: Pascal J. Bourguignon @ 2009-02-04 10:33 UTC (permalink / raw)
  To: help-gnu-emacs

Tassilo Horn <tassilo@member.fsf.org> writes:

> Johan Andersson <johan.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 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)

Pourquoi pas.  Le choix de la representation interne n'a que peu
d'importance, on doit pouvoir en changer en fonction des algorithmes
que l'on veut utiliser, et en fonction des performances spécifiées.

Par contre, que l'on choisisse defstruct, defclass, des a-list, des
p-list (noter que defstruct couvre aussi les listes plates et les
vecteurs, et qu'en emacs lisp, eieio implemente les classes avec des
vecteurs), ce qui compte c'est de cacher ce choix derrière une
abstraction fonctionnelle.

Les accesseurs générés par defstruct ou defclass constituent cette
abstraction fonctionnelle.  Si on choisi une autre representation, il
faut prendre soin de définir soi-même les fonctions nécessaires.

Il y a une petite différence entre les accesseurs définis par
defstruct et defclass: ceux définis par defstruct sont des fonctions
normales, tandis que ceux définis par defclass sont des méthodes de
fonctions génériques, ce qui permet de les appliquer sur des classes
différentes et des sous-classes.    Mais si on ne considère qu'une
classe, on peut spécifier des noms identiques pour les accesseurs dans
les deux cas.


Dans ton exemple set-property défini un interface "dynamique" sur les
slots de l'objet (la p-list), et en plus encapsule la "base de donnée"
des personnes.  Dans certains cas, ça peut être avantageux, d'ailleurs
CLOS (et eieio) définissent un accesseur similaire: (slot-value object
field).  Mais ça sert plus dans le cas où on fait de la
métaprogrammation (par exemple, un sérialiseur / desérialiseur), ou si
il y a des traitements uniformes à faire sur tous les slots.

Dans les autres cas, je crois que c'est plus pratique de définir comme
avec defstruct et defclass, des accesseurs spécifiques.  Évidement, si
on a beaucoup de slots (ou de "classes" de p-list), il vaut mieux
écrire une macro pour les générer.

(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__


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: How to describe something in Lisp?
  2009-02-04 10:33   ` Pascal J. Bourguignon
@ 2009-02-04 11:43     ` Tassilo Horn
  2009-02-05  2:28     ` Pascal J. Bourguignon
  1 sibling, 0 replies; 14+ messages in thread
From: Tassilo Horn @ 2009-02-04 11:43 UTC (permalink / raw)
  To: help-gnu-emacs

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

Hi Pascal,

>> 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)
>
> Pourquoi pas.  Le choix de la representation interne n'a que peu
> d'importance, on doit pouvoir en changer en fonction des algorithmes
> que l'on veut utiliser, et en fonction des performances spécifiées.

I agree, although mon français est très rouillé. ;-)

Bye,
Tassilo





^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: How to describe something in Lisp?
       [not found]     ` <mailman.6723.1233747843.26697.help-gnu-emacs@gnu.org>
@ 2009-02-04 13:26       ` Pascal J. Bourguignon
  0 siblings, 0 replies; 14+ messages in thread
From: Pascal J. Bourguignon @ 2009-02-04 13:26 UTC (permalink / raw)
  To: help-gnu-emacs

Tassilo Horn <tassilo@member.fsf.org> writes:

> pjb@informatimago.com (Pascal J. Bourguignon) writes:
>
> Hi Pascal,
>
>>> 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)
>>
>> Pourquoi pas.  Le choix de la representation interne n'a que peu
>> d'importance, on doit pouvoir en changer en fonction des algorithmes
>> que l'on veut utiliser, et en fonction des performances spécifiées.
>
> I agree, although mon français est très rouillé. ;-)

Oops, sorry!  I didn't switch on my English neurons this morning.
I'll translate this later.

-- 
__Pascal Bourguignon__


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: How to describe something in Lisp?
  2009-02-04 10:33   ` Pascal J. Bourguignon
  2009-02-04 11:43     ` Tassilo Horn
@ 2009-02-05  2:28     ` Pascal J. Bourguignon
  2009-02-05  7:22       ` Johan Andersson
       [not found]       ` <mailman.16.1233818553.17492.help-gnu-emacs@gnu.org>
  1 sibling, 2 replies; 14+ messages in thread
From: Pascal J. Bourguignon @ 2009-02-05  2:28 UTC (permalink / raw)
  To: help-gnu-emacs


And now, translated in English:

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

> Tassilo Horn <tassilo@member.fsf.org> writes:
>
>> Johan Andersson <johan.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 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__


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: How to describe something in Lisp?
  2009-02-05  2:28     ` Pascal J. Bourguignon
@ 2009-02-05  7:22       ` Johan Andersson
       [not found]       ` <mailman.16.1233818553.17492.help-gnu-emacs@gnu.org>
  1 sibling, 0 replies; 14+ messages in thread
From: Johan Andersson @ 2009-02-05  7:22 UTC (permalink / raw)
  To: Pascal J. Bourguignon; +Cc: help-gnu-emacs

[-- Attachment #1: Type: text/plain, Size: 6246 bytes --]

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 <pjb@informatimago.com
> wrote:

>
> And now, translated in English:
>
> pjb@informatimago.com (Pascal J. Bourguignon) writes:
>
> > Tassilo Horn <tassilo@member.fsf.org> writes:
> >
> >> Johan Andersson <johan.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 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__
>

[-- Attachment #2: Type: text/html, Size: 8826 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: How to describe something in Lisp?
       [not found]       ` <mailman.16.1233818553.17492.help-gnu-emacs@gnu.org>
@ 2009-02-06 18:53         ` Ted Zlatanov
  0 siblings, 0 replies; 14+ messages in thread
From: Ted Zlatanov @ 2009-02-06 18:53 UTC (permalink / raw)
  To: help-gnu-emacs

On Thu, 5 Feb 2009 08:22:27 +0100 Johan Andersson <johan.rejeep@gmail.com> wrote: 

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

You might like this, which I think is really nice because it's
self-describing and very clean.  I didn't write it :)

Ted

;;; from J.V. Toups
(defun toups-bang (sym)
  (intern (format "%s!" sym)))
(defun toups-s-cat (sym1 sym2)
  (intern (format "%s-%s" sym1 sym2)))
(defun toups-ques (sym)
  (intern (format "%s?" sym)))

(defmacro defstruquine (name &rest slots)
  (let* ((n-fields (length slots))
   (i 1)
   (out `(progn
     (defun ,(toups-bang name) ,slots
       (list ',(toups-bang name) ,@slots)) 
     (defun ,(toups-ques name) (item)
       (eq (car item) ',(toups-bang name))))))
 (loop for slot in slots do
    (setf out 
    (append out
      (list `(defun ,(toups-s-cat name slot) (item) (elt item ,i)))))
    (setf i (+ i 1)))
 (append out (list nil))))

;; Which can be used thusly:

;; (defstruquine person first-name last-name age)

;; (let ((p (person! "Edward" "Olmos" 61)))
;;   (person-first-name p) ;; is "Edward"
;;   (person-age p) ;; is 62
;;   p) ;; returns (person! "Edward" "Olmos" 61)


^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2009-02-06 18:53 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <mailman.6644.1233674526.26697.help-gnu-emacs@gnu.org>
2009-02-03 15:48 ` How to describe something in Lisp? Andreas Politz
2009-02-03 16:40   ` Pascal J. Bourguignon
2009-02-03 16:44     ` Johan Andersson
2009-02-03 16:54       ` Tassilo Horn
2009-02-03 17:07         ` Johan Andersson
2009-02-03 14:23 Johan Andersson
2009-02-03 16:24 ` Thierry Volpiatto
2009-02-03 16:46 ` Tassilo Horn
     [not found] ` <mailman.6652.1233679633.26697.help-gnu-emacs@gnu.org>
2009-02-04 10:33   ` Pascal J. Bourguignon
2009-02-04 11:43     ` Tassilo Horn
2009-02-05  2:28     ` Pascal J. Bourguignon
2009-02-05  7:22       ` Johan Andersson
     [not found]       ` <mailman.16.1233818553.17492.help-gnu-emacs@gnu.org>
2009-02-06 18:53         ` Ted Zlatanov
     [not found]   ` <7c63jq3319.fsf@pbourguignon.informatimago.com>
     [not found]     ` <mailman.6723.1233747843.26697.help-gnu-emacs@gnu.org>
2009-02-04 13:26       ` Pascal J. Bourguignon

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).