Should mode commands be idempotent?

classic Classic list List threaded Threaded
37 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Should mode commands be idempotent?

Philipp Stephani
Hi,

I think it's generally expected that mode commands (both major and minor) are reasonably idempotent, i.e. calling them twice should have the same effects as calling them once (unless using 'toggle, of course). However, I couldn't find this requirement in the manual, should it be added to the "Modes" section?

Philipp
Reply | Threaded
Open this post in threaded view
|

Re: Should mode commands be idempotent?

Clément Pit-Claudel
On 2017-09-19 21:58, Philipp Stephani wrote:
> I think it's generally expected that mode commands (both major and minor) are reasonably idempotent, i.e. calling them twice should have the same effects as calling them once (unless using 'toggle, of course). However, I couldn't find this requirement in the manual, should it be added to the "Modes" section?

I was not aware of this convention :/

Is there a way inside of a minor mode to check if it was already enabled before the current call, short of having a second variable for that? Most modes I've seen just check whether they're enabled or disabled, and do something based on that:

(define-minor-mode foo-mode
  "Foo."
  :lighter " foo"
  (if foo-mode
      (foo-mode-enable)
    (foo-mode-disable)))

This is often not idempotent (see visual-line-mode for a concrete example).

Clément.

Reply | Threaded
Open this post in threaded view
|

Re: Should mode commands be idempotent?

Stefan Monnier
> Is there a way inside of a minor mode to check if it was already
> enabled before the current call, short of having a second variable
> for that?

It shouldn't be needed: the idempotence should emerge naturally from the
way the code is written, rather than being the result of special tests
to detect that particular situation.


        Stefan


Reply | Threaded
Open this post in threaded view
|

RE: Should mode commands be idempotent?

Drew Adams
In reply to this post by Philipp Stephani
> I think it's generally expected that mode commands (both major and
> minor) are reasonably idempotent, i.e. calling them twice should
> have the same effects as calling them once (unless using 'toggle,
> of course).  However, I couldn't find this requirement in the manual,
> should it be added to the "Modes" section?

It is perhaps typical; i.e., perhaps most modes have that
property.  But I don't see why it "should" be true of modes.
Why should it be a "requirement" or a convention?

People can use a mode for whatever they want.  A mode can
do anything its author wants it to do.  I see that as a
good thing, not a bad thing.

What's the use case for such a restriction?  If it's to
allow for easier program analysis or something, it should
be enough to let people know (if appropriate) that if a
mode is not idempotent then it might be more difficult to
reason about its behavior.  (But reasoning about Lisp
behavior is anyway not something simple - Lisp is not
Haskell.)

The fact that most modes are likely idempotent should be
enough, I think.  Most mode writers don't go out of their
way to make a mode act differently if it is turned on more
than once.

Reply | Threaded
Open this post in threaded view
|

Re: Should mode commands be idempotent?

John Wiegley
In reply to this post by Philipp Stephani
>>>>> "PS" == Philipp Stephani <[hidden email]> writes:

PS> I think it's generally expected that mode commands (both major and minor)
PS> are reasonably idempotent, i.e. calling them twice should have the same
PS> effects as calling them once (unless using 'toggle, of course). However, I
PS> couldn't find this requirement in the manual, should it be added to the
PS> "Modes" section?

Actually, I'm used to turning off minor modes by just calling their mode
function a second time...

--
John Wiegley                  GPG fingerprint = 4710 CF98 AF9B 327B B80F
http://newartisans.com                          60E1 46C4 BD1A 7AC1 4BA2

Reply | Threaded
Open this post in threaded view
|

Re: Should mode commands be idempotent?

Stefan Monnier
> Actually, I'm used to turning off minor modes by just calling their mode
> function a second time...

Of course, the well known and documented toggling behavior of minor
modes is not what he was referring to.

For a minor mode he was thinking of calling it twice with a positive arg
or twice with a negative arg.  


        Stefan


Reply | Threaded
Open this post in threaded view
|

Re: Should mode commands be idempotent?

Stefan Monnier
In reply to this post by Drew Adams
> What's the use case for such a restriction?

I'll return the question: when/why would a major-mode or a minor-mode
not want to be idempotent?  Can you cite at least one example?


        Stefan


Reply | Threaded
Open this post in threaded view
|

Re: Should mode commands be idempotent?

Clément Pit-Claudel
In reply to this post by Stefan Monnier
On 2017-09-20 00:29, Stefan Monnier wrote:
>> Is there a way inside of a minor mode to check if it was already
>> enabled before the current call, short of having a second variable
>> for that?
>
> It shouldn't be needed: the idempotence should emerge naturally from the
> way the code is written, rather than being the result of special tests
> to detect that particular situation.

I'm not too sure: take the example of visual-line-mode: how do you make that idempotent without explicitly checking whether the mode has already been activated?

Reply | Threaded
Open this post in threaded view
|

Re: Should mode commands be idempotent?

Richard Stallman
In reply to this post by Philipp Stephani
[[[ To any NSA and FBI agents reading my email: please consider    ]]]
[[[ whether defending the US Constitution against all enemies,     ]]]
[[[ foreign or domestic, requires you to follow Snowden's example. ]]]

  > I think it's generally expected that mode commands (both major and minor)
  > are reasonably idempotent, i.e. calling them twice should have the same
  > effects as calling them once (unless using 'toggle, of course). However, I
  > couldn't find this requirement in the manual, should it be added to the
  > "Modes" section?

This is the intended behavior, and has been all along.
It would be good to say so explicitly.

For major modes, as long as they work exclusively by setting
buffer-local variables and other per-buffer values (which they should),
kill-all-local-variables takes care of this automatically.

--
Dr Richard Stallman
President, Free Software Foundation (gnu.org, fsf.org)
Internet Hall-of-Famer (internethalloffame.org)
Skype: No way! See stallman.org/skype.html.


Reply | Threaded
Open this post in threaded view
|

Re: Should mode commands be idempotent?

John Wiegley
In reply to this post by Clément Pit-Claudel
>>>>> "CP" == Clément Pit-Claudel <[hidden email]> writes:

CP> I'm not too sure: take the example of visual-line-mode: how do you make
CP> that idempotent without explicitly checking whether the mode has already
CP> been activated?

Also, is there a motivation for introducing this new requirement, seeing as
how we've never had such a restriction in the past?

--
John Wiegley                  GPG fingerprint = 4710 CF98 AF9B 327B B80F
http://newartisans.com                          60E1 46C4 BD1A 7AC1 4BA2

Reply | Threaded
Open this post in threaded view
|

RE: Should mode commands be idempotent?

Drew Adams
In reply to this post by Stefan Monnier
> > What's the use case for such a restriction?
>
> I'll return the question: when/why would a major-mode or a minor-mode
> not want to be idempotent?  Can you cite at least one example?

I don't need to.  There should be some reasons given for making
a change, especially a change that attempt to restrict users,
whether by tooling or convention.

I've already accorded the assumption that most modes do
(already - with no need for such a requirement or convention)
have the requested property of idempotence.  Why reach further?

What's the real problem this request is trying to solve?

I repeat, "Why should it be a "requirement" or a convention?"

(No reason given, so far.)

Reply | Threaded
Open this post in threaded view
|

Re: Should mode commands be idempotent?

Stefan Monnier
In reply to this post by Clément Pit-Claudel
>> It shouldn't be needed: the idempotence should emerge naturally from the
>> way the code is written, rather than being the result of special tests
>> to detect that particular situation.
> I'm not too sure: take the example of visual-line-mode: how do you make that
> idempotent without explicitly checking whether the mode has already
> been activated?

In this case I'd typically do something like the patch below,


        Stefan


diff --git a/lisp/simple.el b/lisp/simple.el
index a3d1cc3864..ed0a29bbcc 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -7053,22 +7053,21 @@ visual-line-mode
   :lighter " Wrap"
   (if visual-line-mode
       (progn
- (set (make-local-variable 'visual-line--saved-state) nil)
- ;; Save the local values of some variables, to be restored if
- ;; visual-line-mode is turned off.
- (dolist (var '(line-move-visual truncate-lines
-       truncate-partial-width-windows
-       word-wrap fringe-indicator-alist))
-  (if (local-variable-p var)
-      (push (cons var (symbol-value var))
-    visual-line--saved-state)))
+ (unless visual-line--saved-state
+  ;; Save the local values of some variables, to be restored if
+  ;; visual-line-mode is turned off.
+  (dolist (var '(line-move-visual truncate-lines
+         truncate-partial-width-windows
+         word-wrap fringe-indicator-alist))
+    (if (local-variable-p var)
+        (push (cons var (symbol-value var))
+      visual-line--saved-state))))
  (set (make-local-variable 'line-move-visual) t)
  (set (make-local-variable 'truncate-partial-width-windows) nil)
  (setq truncate-lines nil
-      word-wrap t
-      fringe-indicator-alist
-      (cons (cons 'continuation visual-line-fringe-indicators)
-    fringe-indicator-alist)))
+      word-wrap t)
+        (add-to-list 'fringe-indicator-alist
+                     (cons 'continuation visual-line-fringe-indicators)))
     (kill-local-variable 'line-move-visual)
     (kill-local-variable 'word-wrap)
     (kill-local-variable 'truncate-lines)


Reply | Threaded
Open this post in threaded view
|

Re: Should mode commands be idempotent?

Stefan Monnier
In reply to this post by John Wiegley
> Also, is there a motivation for introducing this new requirement, seeing as
> how we've never had such a restriction in the past?

In the case of minor modes, I think a good implementation of a minor
mode should be idempotent, because it can easily happen that a minor
mode is enabled twice via different hooks, e.g.:

    (add-hook 'text-mode-hook #'visual-line-mode)
    (add-hook 'xml-mode-hook  #'visual-line-mode)

If you know that xml-mode runs text-mode-hook, you can drop the second
line, but it's good if the second line is just redundant rather
than harmful (after all, some people prefer xml-mode not to run
text-mode-hook).

For major modes, I don't know what is the intention exactly, because I'm
not sure exactly what kind of non-idempotence there is out there.
I think for major modes a more significant issue is to make sure that

    (with-temp-buffer (insert text) (funcall mode))

doesn't do anything undesirable (and currently, we're pretty far from
having such a guarantee, although we do have code like the above at
various places).


        Stefan


Reply | Threaded
Open this post in threaded view
|

RE: Should mode commands be idempotent?

Drew Adams
> > Also, is there a motivation for introducing this new requirement, seeing
> > as how we've never had such a restriction in the past?
>
> In the case of minor modes, I think a good implementation of a minor
> mode should be idempotent, because it can easily happen that a minor
> mode is enabled twice via different hooks, e.g.:
>
>     (add-hook 'text-mode-hook #'visual-line-mode)
>     (add-hook 'xml-mode-hook  #'visual-line-mode)
>
> If you know that xml-mode runs text-mode-hook, you can drop the second
> line, but it's good if the second line is just redundant rather
> than harmful (after all, some people prefer xml-mode not to run
> text-mode-hook).
>
> For major modes, I don't know what is the intention exactly, because I'm
> not sure exactly what kind of non-idempotence there is out there.
> I think for major modes a more significant issue is to make sure that
>
>     (with-temp-buffer (insert text) (funcall mode))
>
> doesn't do anything undesirable (and currently, we're pretty far from
> having such a guarantee, although we do have code like the above at
> various places).

Those are good points.  What should not be taken for granted,
I think, is that enabling twice can have only harmful effects
if a mode is not idempotent.  Not being idempotent only means
that invoking it more than once does not have zero effect.
It does not follow that it has harmful effects.

That's my point, about not imposing a convention or a requirement
without a good reason.  Not that I have in mind some particular
good use case for "beneficial" effects of multiple invocations.
Just that more than one invocation does not necessarily imply
harm instead of benefit.

Reply | Threaded
Open this post in threaded view
|

Re: Should mode commands be idempotent?

Stefan Monnier
In reply to this post by Drew Adams
>> I'll return the question: when/why would a major-mode or a minor-mode
>> not want to be idempotent?  Can you cite at least one example?
> I don't need to.  There should be some reasons given for making
> a change, especially a change that attempt to restrict users,
> whether by tooling or convention.

Philip gave the reason in the first email: "it's generally expected that
mode commands (both major and minor) are reasonably idempotent".

Of course, we wouldn't decide on such a rule without weighing the pros
and cons.  But given that he mentioned one pro, we'd need at the very
least one cons before deciding it's a bad idea (no matter how weak the
pro).

Hence my question again (which is already implicitly contained in
Philip's original message):

    When/why would a major-mode or a minor-mode not want to be idempotent?
    Can you cite at least one example?

And of course, no restriction of the sort discussed here would limit
what the end user can do (we can't technically impose such
a restriction, we can only declare it's bad style).  In practice, such
a restriction would probably allow the user to do more things (because
he can now rely on the idempotency in his code): what you take on one
side, allows to give on the other.


        Stefan


Reply | Threaded
Open this post in threaded view
|

RE: Should mode commands be idempotent?

Drew Adams
> > There should be some reasons given for making a change,
> > especially a change that attempt to restrict users,
> > whether by tooling or convention.
>
> Philip gave the reason in the first email: "it's generally
> expected that mode commands (both major and minor) are
> reasonably idempotent".

If that's _already_ "generally expected", it's because,
as I said, most already _are_ "reasonably idempotent".

Since that is already generally expected, and is already
the case, I don't see a reason for adding a "requirement"
or a "convention" for that.  No reason for such a thing
has been given, so far.  The "reason" you cite just
restates the obvious and echoes the status quo, no?

A reason is what I'm asking for, not a statement that
this is already the case and therefore already generally
expected, but a reason why we should make that already
typical  practice a "requirement" or a "convention".

Having a convention that suggests that most modes should
be idempotent, when most already are, is like the father
of a first-born claiming to have taught his baby to say
"papa", whereas it's the other way 'round: the father's
word "papa" was invented by babies and taught to parents
(and heard by them slightly differently in different
languages).  It's (already) a de facto rule: just a
self-evident truth of experience.

Now if you instead were to propose that _all_ modes
_must_ then yes, it's likely that that's not currently
satisfied.  In that case, I'd ask why _all must_.

But if not - if all you're requesting is a rule that
_most_ _should_, _in general_, then my question is
why come up with such a rule?  What's it for?

Reply | Threaded
Open this post in threaded view
|

Re: Should mode commands be idempotent?

Richard Stallman
[[[ To any NSA and FBI agents reading my email: please consider    ]]]
[[[ whether defending the US Constitution against all enemies,     ]]]
[[[ foreign or domestic, requires you to follow Snowden's example. ]]]

Users expect major modes to be idempotent.  Any time one is not, it
will cause them surprises.  We should treat that as a bug and fix it
to be idempotent.

As for minor modes, it has been pointed out (by Stefan?) that multiple
hooks could enable the same major mode, and the result should be the
same as if just one hook did so.

--
Dr Richard Stallman
President, Free Software Foundation (gnu.org, fsf.org)
Internet Hall-of-Famer (internethalloffame.org)
Skype: No way! See stallman.org/skype.html.


Reply | Threaded
Open this post in threaded view
|

Re: Should mode commands be idempotent?

Richard Stallman
In reply to this post by Philipp Stephani
[[[ To any NSA and FBI agents reading my email: please consider    ]]]
[[[ whether defending the US Constitution against all enemies,     ]]]
[[[ foreign or domestic, requires you to follow Snowden's example. ]]]

  > And any 3rd-party library (if there were any) that might have
  > such a mode function would be well advised to make clear to its
  > users that the function is not idempotent, and explain why:
  > what to expect and why.

I think that approach isn't strong enough to give users predictable
behavior.  We should say that all major modes and minor modes are
idempotent; then, if any fails to be, it will clearly be a bug.

--
Dr Richard Stallman
President, Free Software Foundation (gnu.org, fsf.org)
Internet Hall-of-Famer (internethalloffame.org)
Skype: No way! See stallman.org/skype.html.


Reply | Threaded
Open this post in threaded view
|

Re: Should mode commands be idempotent?

Clément Pit-Claudel
In reply to this post by Stefan Monnier
On 2017-09-22 17:54, Drew Adams wrote:
> Has someone actually reported a problem that s?he ran into by
> encountering an actual mode function that was not idempotent?

I think I mentioned visual-line-mode; the fact that it's not idempotent is a concrete (though minor) problem for me.
I enable it in a hook, and a major mode that I use enables it too.  Due to the way visual-line-mode works, disabling it after this doesn't restore my original settings.

Clément.


Reply | Threaded
Open this post in threaded view
|

Re: Should mode commands be idempotent?

Clément Pit-Claudel
In reply to this post by Stefan Monnier
On 2017-09-21 00:25, Stefan Monnier wrote:
>>> It shouldn't be needed: the idempotence should emerge naturally from the
>>> way the code is written, rather than being the result of special tests
>>> to detect that particular situation.
>> I'm not too sure: take the example of visual-line-mode: how do you make that
>> idempotent without explicitly checking whether the mode has already
>> been activated?
>
> In this case I'd typically do something like the patch below

Thanks. AFAICT visual-line--saved-state acts mostly as a proxy for checking whether the mode was enabled, but I also see that it's not quite the same as not running the mode function if it's already enabled.

Interestingly, what makes visual-line-mode not idempotent in the first place is that it goes to great lengths to save user settings and restore them.  Had it not done this (that is, had it just killed the local variable bindings when disabled), it would have been idempotent but more annoying to use.

It isn't even obvious that the current behavior (even with your patch) is what we want, anyway: what if I have another mode foo-mode that touches the same variables as visual-line-mode? Then I can run (visual-line-mode), (foo-mode), (visual-line-mode -1), and then which state should I be in?  There's a more general problem here, and it's not an easy one :/

12