help-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Always return a list


From: Tim X
Subject: Re: Always return a list
Date: Fri, 26 Nov 2010 10:03:10 +1100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.0.50 (gnu/linux)

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

> Hey,
>
> I have a struct. To make it simple, lets assume this is the struct:
>   (defstruct package name deps)
>
> The deps slot is for the package dependencies. Example of packages:
>   (make-package :name "one" :deps "two")
>   (make-package :name "two" :deps '("three" "four"))
>
> To make it easier to use the dependencies from the code, when I
> call (package-deps package) I always want it to return a list. So:
>
>   (let ((one (make-package :name "one" :deps "two"))
>         (two (make-package :name "two" :deps '("three" "four"))))
>
>     (print (package-deps one)) ;; => ("two")
>     (print (package-deps two)) ;; => ("three" "four")
>     )
>
> I tried doing this using an advice, with no success:
>   (defadvice package-deps (around package-deps-around)
>     (let ((deps ad-do-it))
>       (if (listp deps) deps (list deps))))
>   (ad-activate 'package-deps)
>
> Can I do this in a way that doesn't require that much of a hack?
>

If your structure is not much more complicated that your example, I
would not even bother with defstruct. The defstruct facility is not a
core elisp data type, but rather a partial implementation of the CL
defstruct type. This implementation is based on either a list or vector
type. Unless your also using many other cl functions, you will probably
get more precisely what you want by defining your own functions to
create and manipulate either a list or vector representing
your data and avoid the need to require the whole cl package. Writing
the functions to create, set, get, test and print a simple structured
list or vector like your example is quite trivial and you would have
more control over things. 

If what you need is actaully more complex, I would suggest looking into
the eieo package. 

There are a couple of things you could do if you want to stick with defstruct

1. Create a custom constructor for your struct that sets the deps slot
to always be a list. You will need to set the default constructor to nil
to stop a default from being created. Your custom constructor(s) will
need to handle the cases where it is called with a deps argument that is
nil, a single atomic element and a list - converting all to a list.

Ensure all calls to set a slot pass it a list, i.e.

   (let ((one (make-package :name  :deps (list "two")))
         (two (make-package :name "two" :deps (list "three" "four"))))

With these two restrictions, you can make the assumption the deps slot
is always a list and dispenc with the tests. 

2. Just define your own print-deps funtion. Instead of 

     (print (package-deps one)) ;; => ("two")
     (print-deps one) ;; => ("two")

3. Redefine package-deps to include the test/conversion of the slot to
always be a list. This approach has the advantage that querying the deps
slot will provide consistent results regardless of where it is used.

4. If you feel adventurous, this could be a good macro learning
exercise. Create a mydefstruct macro, which would define the base list
structure, the constructor, setters, getters and tests etc. Possibly not
a bad example for exploring the possibilities that macros provide. 

5. Simplest of all - just ensure you always create new struct instances
with a list argument for the deps slot and ensure in any setf
operations, you set it to either a list or nil. The core of the problem
you have is that currently, your deps slot may be a single atom or it
may be a list. If you ensure it is always a list, results will be
consistent. Of course, this does leave a good place for bugs to lurk as
it requires the programmer to know/remember this.

Tim



-- 
tcross (at) rapttech dot com dot au


reply via email to

[Prev in Thread] Current Thread [Next in Thread]