emacs-devel
[Top][All Lists]
Advanced

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

Default value of variables named `*-function' [was: Change in files.e


From: Drew Adams
Subject: Default value of variables named `*-function' [was: Change in files.el]
Date: Tue, 31 Jan 2017 10:40:10 -0800 (PST)

> I would appreciate the consistency of variables named
> with -function/-predicate suffixes to have a function
> has a default value. Being able to use add-function on
> just variables is very convenient (I use it to tweak
> region-extract-function). The default values also serve
> as good examples.

My comment here is not about providing an implicit/automatic
default value.  (I doubt that that is needed or a good idea,
but I could be wrong.)

My comment is that the default value for a given variable
whose value is intended to be a (single) function _should
usually_ be function `ignore'.  Not implicitly (see previous
paragraph) but explicitly: (defvar foo-function 'ignore).

TL;DR:
Start `-function' vars out with value `ignore', possibly
advised, so `remove-function' can get you back to a no-op.

But please read on...

If the default value needs to be a function that does
something (which is common), then instead of using, as
the default value, the function that provides the default
behavior, use `ignore' as the default value AND advise it
immediately with the function that provides the default
behavior.

Why?  Because then you can use `remove-function' to get
a no-op.  You need not set or bind the variable to
'ignore' to do that; just repeated `remove-function'
will get you to the no-op `ignore'.

`remove-function' is analogous to setting a cons-valued 
variable to its cdr.  If `(cdr (last xs))' is not nil
then xs is not a true list (it is dotted).  Using
`ignore' as the starting point for function advice is like 
using () as the starting point (the last cdr) of a list.
Function  `ignore' is more or less to advice what () is to
a list of functions - an identity element.

Advising the function that is the value of a `*-function'
variable gives you the possibility of, in effect,
applying any number of functions.  Using `:after-while'
or `:before-until', for example, gives you much the same
effect as you get with a normal or abnormal hook (as
opposed to a `*-function' hook).

But there is this big difference between a hook whose
value is a list of functions (a la `add-hook') and a
`*-function' variable whose value is a function that is
advised with, say, `:after-while':

With a hook that is a list of functions, you can remove
them all using `remove-hook'.  With an advised function,
the value is always a function.  You can of course
change the value to `ignore', but you cannot, just using
`remove-function', get to a no-op function.

Unless, that is, the starting point (the default value)
is `ignore'.  Hence my suggestion: Start with `ignore',
advising it immediately if some default behavior is
called for.

That lets users and code use just `add-function' and
`remove-function' to get the effect they have with
`add-hook' and `remove-hook': a (possibly empty)
sequence of functions applied in order.

I pointed this out in a parenthical remark in this post:
http://lists.gnu.org/archive/html/emacs-devel/2016-10/msg00415.html

    (Note that one difference from a hook is that a hook
    does not privilege the first hook function in any way
    (or the last, depending on how you look at it).
    Removing advice is not equivalent to `remove-hook'.
    It never "empties the hook" completely - the function
    that was advised is still there after removal of all
    advice.)

Here's a case in point:

Variable `isearch-filter-predicate' has a function value
or nil.  This is legacy.  Users and code can still bind
it or set it to nil.

But they cannot set it to a list of functions.  IOW, it
is basically a single-function variable (which would
normally be named `*-function') - except for the ability
to use nil instead.

I have code that lets you (interactively) add to, remove
from, etc. the behavior of `isearch-filter-predicate' on
the fly.  For more info, see
https://www.emacswiki.org/emacs/DynamicIsearchFiltering.

You can hit a key to change the current filter predicate
in ways like these:

C-z -   Remove a predicate that you specify (default:
        last-added predicate).
C-z &   Add a predicate, AND-ing it as an `:after-while'
        filter.
C-z %   Add a predicate that requires search hits to
        match a regexp you provide.
C-z ||  Add a predicate, OR-ing it as a `:before-until'
        filter.
C-z |1  Replace the last-added filter by OR-ing it with
        another.
C-z ~~  Complement the overall filter predicate.
C-z ~1  Replace the last-added filter by its complement.
C-z !   Set the overall filter predicate.
C-z 0   Reset the overall filter predicate to its default
        value (`isearch-filter-visible').
C-z c   Add a predicate that limits search between two
        columns that you specify.
C-z @   Add a predicate that constrains searching within a 
        given distance of (near) another search pattern
        (you specify the distance and pattern).

Since the default value of `isearch-filter-predicate' is
not `ignore' but `isearch-filter-visible', you cannot just
add and remove advice to get a no-op behavior or a behavior
that is not a modification of `isearch-filter-visible'.
Instead, you must set (or bind) `isearch-filter-predicate'.

This is a defect, resulting from legacy: (1) using nil
instead of `ignore' as the no-op filter and (2) using
`isearch-filter-visible' instead of `ignore' as the
default filter.

It would be nice to be able to just add & remove advice,
to control the behavior.  But that always acts on
function `isearch-filter-visible', not on `ignore'.

Code should of course be _able_ to set or bind the
variable value - no question about that.  But things
are more flexible if you can _also_ do pretty much
anything to modify the behavior using just advice.

If my suggestion were applied to the case of
`isearch-filter-predicate' then (1) it would be renamed
(e.g., to `isearch-filter-function'), and (2) its
default value would be `ignore'.

Now, because `isearch-filter-predicate' is legacy,
(1) the variable renaming would need to deprecate (but
continue to support) the old name, and (2) the variable
value nil would need to continue to be supported.

This is just an example, to try to get across the
suggestion that (1) we use `ignore' as the (explicit,
not automatic/implied) default value of variables named
`*-function', and (2) for any such variable whose
default behavior needs to be a no-op, we immediately
(i.e., by default) advise it to provide that default
behavior.

That gives users & code two ways to change it to a no-op:
(a) set the variable value to `ignore' or (b) remove all
advice from the default function (`ignore').

In a way, this amounts to saying that what is said
(correctly) in (elisp) `Advice combinators' about
various kinds of advice being "comparable for
single-function hooks to (add-hook..." is not also true
for `remove-function' - unless the starting point is
function `ignore'.  Repeatedly applying `remove-hook'
will eventually get you to (), but repeatedly applying
`remove-function' will not get you to `ignore', unless...

You might point out that if a user or code _does_ just
set the value of such a variable to some other function
than `ignore' then `remove-function' will _not_ get back
to `ignore'.  Clearly.  The effect of setting or (with a
little more work) binding can be had using just advice,
but nothing prevents someone from not using advice to
change the value.

Dunno whether I was clear or I am missing something
important.  But WDOT?



reply via email to

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