unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Some improvements for cl-flet
@ 2021-09-11 12:51 akater
  2021-09-11 23:32 ` Michael Heerdegen
  0 siblings, 1 reply; 6+ messages in thread
From: akater @ 2021-09-11 12:51 UTC (permalink / raw)
  To: emacs-devel; +Cc: Stefan Monnier

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

I've implemented some fixes for ~cl-flet~ necessary to address an issue
with ~cl-generic~ but also improving ~cl-flet~ proper.  Before I post
the patch, I'd like to clarify one issue.

~cl-flet~'s ~(func exp)~ syntax, as described and implemented, is
incompatible with ~flet~ syntax as specified in Common Lisp.  Namely,
~(func exp)~ syntax does not allow to distinguish between a form ~exp~
returning a function and an arglist ~exp~ for a function ~func~ that
always returns ~nil~.

Consider the following valid (but stylistically poor) Common Lisp code:
#+begin_src lisp :wrap example lisp
(flet ((f #'g))
  (f t t))
#+end_src

#+RESULTS:
#+begin_example lisp
NIL
#+end_example

If I understand the purpose of ~cl-lib~ correctly, corresponding
~cl-flet~ form should return ~nil~ as well.  However, it errors:
#+begin_src emacs-lisp :results code :wrap example emacs-lisp
(condition-case err (cl-flet ((f #'g))
                      (f t t))
  (t err))
#+end_src

#+RESULTS:
#+begin_example emacs-lisp
(void-function g)
#+end_example

In case it's not clear what's going on: the previous example is
equivalent to the following one, only with ~function~, ~g~ replaced with
~x~, ~y~ correspondingly:
#+begin_src lisp :wrap example lisp
(flet ((f (x y)))
  (f t t))
#+end_src

#+RESULTS:
#+begin_example lisp
NIL
#+end_example

but due to ~(func exp)~ syntax, ~(x y)~ is presumed to be a form meant
to return a function, rather than an arglist, and so
#+begin_src emacs-lisp :results code :wrap example emacs-lisp
(condition-case err (cl-flet ((f (x y)))
                      (f t t))
  (t err))
#+end_src

#+RESULTS:
#+begin_example emacs-lisp
(void-function x)
#+end_example

The syntax of ~flet~ could only be compatible with ~cl-flet~'s ~(func
exp)~ syntax when ~exp~ is presumed to be a non-list.  For example,
~exp~ could be a symbol.

The following expression could also be unambiguously interpreted in
style of ~(func exp)~ because ~nil~ is not a valid function argument:
#+begin_src emacs-lisp :results code :wrap example emacs-lisp
(cl-flet ((f (lambda nil nil)))
  (f))
#+end_src

#+RESULTS:
#+begin_example emacs-lisp
nil
#+end_example

However I don't think it's worth it to use complicated rules to
distinguish such cases.  ~flet~ just has its own syntax, incompatible
with the (Scheme-inspired, likely) idea of binding a function to a
variable.  Such complications would keep ~cl-flet~ unfit for use in
macroexpanded (and otherwise generated) code.  Last but not least,
~(flet ((f (lambda nil nil))) (f))~, etc., is not a valid Common Lisp
code.

Note also that the following is valid (and stylistically fine) Common
Lisp code:
#+begin_src lisp :wrap example lisp
(flet ((f ()))
  (f))
#+end_src

#+RESULTS:
#+begin_example lisp
NIL
#+end_example

However, evaluating corresponding ~cl-flet~ form errors:
#+begin_src emacs-lisp :results code :wrap example emacs-lisp
(condition-case err (cl-flet ((f ()))
                      (f))
  (t err))
#+end_src

#+RESULTS:
#+begin_example emacs-lisp
(void-function nil)
#+end_example

Finally, note that (an equivalent) ~(func exp)~ syntax could not be
adopted by at all by ~cl-macrolet~, as macrolet forms support
destructuring lambda lists.  For example, the following is a valid (but
stylistically poor) Common Lisp code:
#+begin_src lisp :wrap example lisp
(macrolet ((f (lambda () 'x)))
  (f t nil (t t)))
#+end_src

#+RESULTS:
#+begin_example lisp
NIL
#+end_example

Given all this, I think ~(func exp)~ should be dropped from ~cl-flet~.
My patch (already discussed with Stefan Monnier to some extent)
introduces function ~cl--expand-flet~ which retains the functionality
currently provided by ~(func exp)~, in an unambiguous way.  I suggest to
move it there, away from ~cl-flet~.

Do you agree?

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 865 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Some improvements for cl-flet
  2021-09-11 12:51 Some improvements for cl-flet akater
@ 2021-09-11 23:32 ` Michael Heerdegen
  2021-09-12  3:35   ` akater
  0 siblings, 1 reply; 6+ messages in thread
From: Michael Heerdegen @ 2021-09-11 23:32 UTC (permalink / raw)
  To: emacs-devel

Hello akater,

I'm having problems to understand what you want to do and why.

I see that a binding like (f (x y z)) is ambiguous.  But isn't that a
minor problem?  Who ever wants to define a local function that always
just returns nil?  Ok, it can happen, very rarely, but then

  (f (x y z) nil)

works and is much better readable.

> Given all this, I think ~(func exp)~ should be dropped from ~cl-flet~.

And I don't understand why this minor annoyance justifies such a radical
measure, unless I misread that.  I'm often using that syntax.

> My patch (already discussed with Stefan Monnier to some extent)
> introduces function ~cl--expand-flet~ which retains the functionality
> currently provided by ~(func exp)~, in an unambiguous way.  I suggest to
> move it there, away from ~cl-flet~.

Now I'm even more confused: do you suggest to factor the code somehow?

Or would I have to use `cl--expand-flet' instead of `cl-flet' in the
future to get the same behavior as now?  That would be strange.

Sorry if I'm missing something.  It would be helpful to see a patch or
some code, even if it is just a draft.


Michael.




^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Some improvements for cl-flet
  2021-09-11 23:32 ` Michael Heerdegen
@ 2021-09-12  3:35   ` akater
  2021-09-12 15:38     ` Stefan Monnier
  2021-09-13  0:14     ` Michael Heerdegen
  0 siblings, 2 replies; 6+ messages in thread
From: akater @ 2021-09-12  3:35 UTC (permalink / raw)
  To: Michael Heerdegen, emacs-devel

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

Michael Heerdegen <michael_heerdegen@web.de> writes:

> I'm having problems to understand what you want to do and why.

1. To understand which variation of the patch I should post.

2. Hopefully to convince someone that this (func exp) was a terrible
idea.

> I see that a binding like (f (x y z)) is ambiguous.  But isn't that a
> minor problem?

No.  Syntax ambiguity is always a huge inconvenience.

- If the purpose of cl-lib is to provide a compatibility layer for CL,
  then this is simply not compatible, on top of being ambiguous.

- Such irregularities make code harder to port.

- Such irregularities make it harder to write code programmatically.
  (This also addresses the argument about readability and why would
  anyone write code like this.)

- Last but not least, such irregularities introduce a non-technical
  problem: whether something should be ported to cl-... as is, or a
  degree of artistic freedom is allowed.  If yes, to which extent?  Is
  it worth it to waste everyone's time arguing about each such instance
  in the future and synchronizing their perception?  I'm absolutely
  certain it's not worth it.  The sole point of standards is to be
  followed and thus remain reliable.  Unless they can't be followed for
  technical reasons, in which case it should be noted that those are
  actually limitations, hopefully to be lifted one day.

> And I don't understand why this minor annoyance justifies such a radical
> measure, unless I misread that.

If anything, it's actually (func exp) that is a radical departure from
flet semantics.  I do my best at offering my arguments but in fact it is
one introducing something that is both (!) ambiguous and incompatible
with CL who has (OK, had; it's too late for me now) the burden of proof.

> would I have to use `cl--expand-flet' instead of `cl-flet' in the
> future to get the same behavior as now?

It depends.  Sometimes it's better to use let.  Sometimes, like in our
case with cl-generic, an expander is the most appropriate.

> That would be strange.

With all due respect, I do not accept “strange” for an argument and I
never offer such arguments myself.  “Strange” is subjective.  cl-flet is
incompatible with flet (and rest of the points) --- that's objective.

Even if I fail at convincing anyone that it should be dropped, I do
wonder if this was indeed a Scheme influence.

I conveyed all the reasons I could.  If it's not convincing enough,
there is no point in discussing this further, and I'll soon post a
variation of the patch that keeps this syntax in cl-flet as is.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 865 bytes --]

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Some improvements for cl-flet
  2021-09-12  3:35   ` akater
@ 2021-09-12 15:38     ` Stefan Monnier
  2021-09-13  0:14     ` Michael Heerdegen
  1 sibling, 0 replies; 6+ messages in thread
From: Stefan Monnier @ 2021-09-12 15:38 UTC (permalink / raw)
  To: akater; +Cc: Michael Heerdegen, emacs-devel

> - If the purpose of cl-lib is to provide a compatibility layer for CL,
>   then this is simply not compatible, on top of being ambiguous.

That statement is true, but it doesn't apply here because the purpose of
cl-lib is not really to provide a compatibility layer for CL (even less
so than the old `cl.el`, since all the identifiers are different due
their `cl-` prefix).


        Stefan




^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Some improvements for cl-flet
  2021-09-12  3:35   ` akater
  2021-09-12 15:38     ` Stefan Monnier
@ 2021-09-13  0:14     ` Michael Heerdegen
  2021-09-13  2:26       ` Stefan Monnier
  1 sibling, 1 reply; 6+ messages in thread
From: Michael Heerdegen @ 2021-09-13  0:14 UTC (permalink / raw)
  To: akater; +Cc: emacs-devel

akater <nuclearspace@gmail.com> writes:

> > I see that a binding like (f (x y z)) is ambiguous.  But isn't that a
> > minor problem?
>
> No.  Syntax ambiguity is always a huge inconvenience.

I see your point.  OTOH, removing the expression syntax case would be a
backward incompatible change potentially break existing code - right?

> Even if I fail at convincing anyone that it should be dropped, I do
> wonder if this was indeed a Scheme influence.

A code example (to illustrate what cases we talk about) could look like:

#+begin_src emacs-lisp
(cl-flet ((multilinep (apply-partially #'string-match-p "\n")))
  (pcase something
    ((and (pred stringp) (pred multilinep)) ...)
    ...))
#+end_src

It's useful to be able to bind the result of some expression to a
symbol's function binding.  AFAIR I was one of those who wanted
`cl-flet' to support this, and I had not been inspired by Scheme much.

Personally I wouldn't mind when this functionality would be provided by
some other form, but there is backward compatibility.  And don't you
think that cl-lib (see Stefan's answer) differs from CL much more in
other aspects?

You can find discussions about to which degree cl-lib should be kept an
as strict as possible emulation of Common Lisp in the emacs dev and/or
bug mailing lists.  There were different opinions.  Undoubtedly the main
purpose of cl-lib today is just for writing Emacs Lisp code, however.

Michael.



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Some improvements for cl-flet
  2021-09-13  0:14     ` Michael Heerdegen
@ 2021-09-13  2:26       ` Stefan Monnier
  0 siblings, 0 replies; 6+ messages in thread
From: Stefan Monnier @ 2021-09-13  2:26 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: akater, emacs-devel

> (cl-flet ((multilinep (apply-partially #'string-match-p "\n")))
>   (pcase something
>     ((and (pred stringp) (pred multilinep)) ...)
>     ...))

FWIW, this is better written as

    (cl-flet ((multilinep (s) (string-match-p "\n" s)))
      ...)

which is both shorter and more efficient.  But there are other cases
where the "simple" form of `cl-flet` is more efficient and the CL
version would need to be of the form

    (flet ((FOO (&rest args) (apply BAR args)))
      ...)

or worse

    (let ((bar (BAR BAZ))
      (flet ((FOO (&rest args) (apply bar args)))
        ...)

which would introduce a significant inefficiency and which can be
difficult for the compiler to optimize back to the efficiency of the
code that our `cl-flet` gets without any effort.

Its true that our `cl-flet` introduces an incompatibility with CL's
semantics, but I considered that it's sufficiently minor that the
tradeoff is worth it (as compared to introducing a separate new form).


        Stefan




^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2021-09-13  2:26 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-11 12:51 Some improvements for cl-flet akater
2021-09-11 23:32 ` Michael Heerdegen
2021-09-12  3:35   ` akater
2021-09-12 15:38     ` Stefan Monnier
2021-09-13  0:14     ` Michael Heerdegen
2021-09-13  2:26       ` Stefan Monnier

Code repositories for project(s) associated with this 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 NNTP newsgroup(s).