unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Daniel Colascione <dancol@dancol.org>
To: Stefan Monnier <monnier@IRO.UMontreal.CA>
Cc: emacs-devel@gnu.org
Subject: Re: [Emacs-diffs] trunk r116995: cl-lib defstruct introspection
Date: Mon, 21 Apr 2014 15:26:21 -0700	[thread overview]
Message-ID: <53559B0D.3070505@dancol.org> (raw)
In-Reply-To: <jwvoazunyg1.fsf-monnier+emacsdiffs@gnu.org>

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

On 04/21/2014 03:03 PM, Stefan Monnier wrote:
>> That's new. Using the whole ChaneLog message has been a recommendation,
>> but never a requirement.
> 
> For `elpa', that's true, but for `emacs' it's always been a requirement,
> on the premise that this should/will allow us to drop the ChangeLog
> files at some point.

Thanks for the clarification.

>> Now there's one more step on the commit path, and a useless one at
>> that: the changelog entry is available in the change itself and in the
>> message to the mailing list.
> 
> C-x v v can copy the message from ChangeLog for you (and set Author:
> and Fixes: at the same time), so it's not so bad.

Can it? I tried it in vc-dir and got a completely unrelated ChangeLog hunk.

> The way to fix this, is to make ChangeLog unneeded.  First step on this
> path is to provide some way to make `C-x 4 a' usable without ChangeLog.
> 
>>>> +The @code{cl-defstruct} package also provides a few structure
>>>> +introspection functions.
>>> I'm curious: when/where did you bump against a need for that?
>> I have a few private macros that lexically bind structure slots,
> 
> Which part makes it impossible/impractical to use standard accessors for
> that?

:conc-name, for starters. Also, :read-only, although you could argue
that you shouldn't go around mutating read-only slots anyway.

>> and this information is also needed for some interface-generation work
>> I'm thinking of doing.
> 
> Not sure what "interface-generation" means, but it sounds interesting.

I'm just playing around at this point --- but the basic idea is that we
need some way to connect independent Emacs components, and this
mechanism should be more structured than some kind of
do-everything-in-a-giant-cond handler function, but much lighter and
more comprehensible than EIEIO.

One approach is to define COM-like (wait! keep reading!) "interface"
structures that bundle useful functions into composeable pieces, then
have Emacs components interact in terms of these interfaces. We can make
them very lightweight, and let all interfaces inherit from a common
interface that lets callers ask for additional functionality:

(iface-declare (iface-base nil)
  "Interface from which all others inherit."
  ((iface-query instance interface)
   "Get an implementation of INTERFACE from INSTANCE."))

For example, we could define some project interfaces like this:

(iface-declare (project iface-base)
  ((project-get-root-directory project )
   "Find the root directory of PROJECT.")
  ((project-get project property)
   "Get a project property PROPERTY.")
  ((project-put project property value)
   "Set a project property PROPERTY to VALUE."))

(iface-declare (project-c iface-base)
  ((project-c-include-directories project file)
   "Return a list of include directories for FILE in PROJECT.
FILE is a fully-qualified name."))

iface-declare defines trivial wrapper functions that make it convenient
to call through interface fields. So the generated definition of
project-get, for example, would look like this:

(defun project-get (inst &rest xargs)
    "Get a project property PROPERTY."
    (declare (advertised-calling-convention (project property))
    (apply (cl-struct-slot-value 'project 'get inst) xargs))

If you have a project and want to see whether it supports finding C
include paths, you'd just use iface-query with 'project-c, and if that
succeeds, call (project-c-include-directories my-c-project file).

You'd actually *make* interface instances by building an interface
struct as you would any other struct and supply closures (e.g., the
result of `apply-partially') for the slot values so that you can
maintain state (or not) between subsequent calls on the same interface
instance.

Anyway, all of this would be very lightweight and (I think) resolvable
completely at compile time.

Note that it's probably not a good idea to go even simpler and have,
say, my-git-project inherit from project directly: what if the size of
project changes? (Things would break because cl-defstruct accessors
hardcode field offsets, and that's a good thing.) What if you want to
implement multiple pieces of optional functionality? (Sure, you can make
some struct fields nullable, but then you have to provide some way to
query whether a field is actually implemented.)

Better to just define additional interfaces for optional functionality.
The interface approach seems decently light and extensible, and the
implementation complexity is low.

>>>> +@defun cl-struct-set-slot-value struct-type slot-name inst value
>>> We don't need this, since we can always use setf instead.
>> So? We have both (setf (aref ...) ...) and (aset ...).
> 
> That's only because (setf (aref ...) ...) needs to macroexpand to something.
> [ It's one of the differences between Common-Lisp and Elisp.  ]
> 
> In your case, (setf (cl-struct-slot-value ...) ...) can macroexpand to
> something without needing cl-struct-set-slot-value.  Actually, in order
> for (incf (cl-struct-slot-value ...)) not to compute the offset twice,
> (setf (cl-struct-slot-value ...) ...) will end up expanding to something
> else than a call to cl-struct-set-slot-value.
> 
>> That test was there in cl-check-type. The test doesn't make sense to me
>> either. We should drop it in both places if we drop it in cl-the.
> 
> Great, let's drop it then.  Thanks.

Will do. That test never made much sense to me anyway.

>>>> +(cl-define-compiler-macro cl-struct-slot-value
>>> Please use (declare (compiler-macro ..)).
>> Why? In both cases, the compiler macro is written out-of-line and in
>> both cases, we just stick the compiler macro on the symbol's plist.
> 
> Because that's the style we use in Elisp.

Well, recently. ;-) It wasn't too long ago that the style was to
let-bind dozens of variables at the top of huge functions and setq them
everywhere.

> Note that (declare (compiler-macro ..)) can provide the compiler-macro
> "inline" or "out-of-line".

Sure, but it'll be really ugly if it's inline and relatively large (as
the macros here are). It goes with a trend I've noticed in the APIs
you've implemented, though: you seem to prefer having people defun
regular functions and combine them in interesting ways (e.g., with
advice-add) instead of using top-level definition forms directly (e.g.,
defadvice). I largely agree with your approach.

> 
>>> I guess this goes back to the earlier question about when/where the use
>>> for this functionality came up.
>> Unless we're using this functionality in generated code where, while the
>> slot is constant, it's more convenient to use that slot's name than to
>> try to determine the accessor name.
> 
> Ah, so it's for code generated based on cl-struct-slot-info?
> Right, that makes sense.


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 884 bytes --]

  reply	other threads:[~2014-04-21 22:26 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <E1Wbhqn-0001CJ-4X@vcs.savannah.gnu.org>
2014-04-20 12:49 ` [Emacs-diffs] trunk r116995: cl-lib defstruct introspection Stefan Monnier
2014-04-23 12:56   ` Stefan Monnier
2014-04-21 15:38 ` Stefan Monnier
2014-04-21 17:40   ` Daniel Colascione
2014-04-21 22:03     ` Stefan Monnier
2014-04-21 22:26       ` Daniel Colascione [this message]
2014-04-22  2:03         ` Stefan Monnier
2014-04-22  2:07           ` Daniel Colascione
2014-04-22  3:28             ` Stefan Monnier
2014-04-23  3:18       ` Declaim and proclaim (Was: Re: [Emacs-diffs] trunk r116995: cl-lib defstruct introspection) Daniel Colascione
2014-04-23 13:14         ` Declaim and proclaim Stefan Monnier

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

  List information: https://www.gnu.org/software/emacs/

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

  git send-email \
    --in-reply-to=53559B0D.3070505@dancol.org \
    --to=dancol@dancol.org \
    --cc=emacs-devel@gnu.org \
    --cc=monnier@IRO.UMontreal.CA \
    /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 public inbox

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

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