all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Tim X <timx@nospam.dev.null>
To: help-gnu-emacs@gnu.org
Subject: Re: Always return a list
Date: Fri, 26 Nov 2010 10:03:10 +1100	[thread overview]
Message-ID: <87tyj5f1ld.fsf@puma.rapttech.com.au> (raw)
In-Reply-To: mailman.0.1290679543.16375.help-gnu-emacs@gnu.org

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	other threads:[~2010-11-25 23:03 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <mailman.0.1290679543.16375.help-gnu-emacs@gnu.org>
2010-11-25 23:03 ` Tim X [this message]
2010-11-25 10:05 Always return a list Johan Andersson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87tyj5f1ld.fsf@puma.rapttech.com.au \
    --to=timx@nospam.dev.null \
    --cc=help-gnu-emacs@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.