bug#30078: 27.0.50; Use lexical-binding for M-:

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

bug#30078: 27.0.50; Use lexical-binding for M-:

Stefan Monnier
Package: Emacs
Version: 27.0.50


I think we should slowly move towards making lexical-binding the default
and we could start by making M-: always use lexical binding.
WDYT?


        Stefan


diff --git a/lisp/simple.el b/lisp/simple.el
index 4c69e0f6ff..fa31ef9f0f 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -1608,12 +1608,12 @@ eval-expression
          (eval-expression-get-print-arguments current-prefix-arg)))
 
   (if (null eval-expression-debug-on-error)
-      (push (eval exp lexical-binding) values)
+      (push (eval exp t) values)
     (let ((old-value (make-symbol "t")) new-value)
       ;; Bind debug-on-error to something unique so that we can
       ;; detect when evalled code changes it.
       (let ((debug-on-error old-value))
- (push (eval (macroexpand-all exp) lexical-binding) values)
+ (push (eval (macroexpand-all exp) t) values)
  (setq new-value debug-on-error))
       ;; If evalled code has changed the value of debug-on-error,
       ;; propagate that change to the global binding.



Reply | Threaded
Open this post in threaded view
|

bug#30078: 27.0.50; Use lexical-binding for M-:

Michael Heerdegen
Stefan Monnier <[hidden email]> writes:

> I think we should slowly move towards making lexical-binding the default
> and we could start by making M-: always use lexical binding.
> WDYT?

Why not?

But maybe we should keep this consistent with C-x C-e in *scratch* to
avoid confusion (a la "why do the evaluation results differ?"),
i.e. enable lexical-binding in *scratch* at the same time?


Michael.



Reply | Threaded
Open this post in threaded view
|

bug#30078: 27.0.50; Use lexical-binding for M-:

Stefan Monnier
>> I think we should slowly move towards making lexical-binding the default
>> and we could start by making M-: always use lexical binding.
>> WDYT?
> Why not?
> But maybe we should keep this consistent with C-x C-e in *scratch* to
> avoid confusion (a la "why do the evaluation results differ?"),
> i.e. enable lexical-binding in *scratch* at the same time?

Good idea.


        Stefan



Reply | Threaded
Open this post in threaded view
|

bug#30078: 27.0.50; Use lexical-binding for M-:

Stefan Monnier
>>> I think we should slowly move towards making lexical-binding the default
>>> and we could start by making M-: always use lexical binding.
>>> WDYT?
>> Why not?
>> But maybe we should keep this consistent with C-x C-e in *scratch* to
>> avoid confusion (a la "why do the evaluation results differ?"),
>> i.e. enable lexical-binding in *scratch* at the same time?
> Good idea.

I figured M-x ielm would qualify as well.
How 'bout this patch, then?


        Stefan


diff --git a/etc/NEWS b/etc/NEWS
index f6f36dfc85..d7e84eca7f 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -52,6 +52,10 @@ to reduce differences between developer and production builds.
 
 * Changes in Emacs 27.1
 
+** Lexical binding is now used when evaluating interactive Elisp forms
+More specifically, lexical-binding is used for M-:, M-x ielm, as well
+as in the *scratch* buffer.
+
 ---
 ** The new option 'tooltip-resize-echo-area' avoids truncating tooltip text
 on GUI frames when tooltips are displayed in the echo area.  Instead,
diff --git a/lisp/ielm.el b/lisp/ielm.el
index fb285e80f6..cad1bded4f 100644
--- a/lisp/ielm.el
+++ b/lisp/ielm.el
@@ -408,8 +408,7 @@ ielm-eval-input
                                 (setf standard-output new-standard-output))
                               (kill-buffer (current-buffer))
                               (set-buffer wbuf)
-                              (setq result
-                                    (eval form lexical-binding))
+                              (setq result (eval form t))
                               (setq wbuf (current-buffer))
                               (setq
                                ielm-temp-buffer
diff --git a/lisp/server.el b/lisp/server.el
index ac0d701851..918197ab44 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -1294,7 +1294,7 @@ server-execute
  ((functionp initial-buffer-choice)
   (funcall initial-buffer-choice)))))
       (switch-to-buffer
-       (if (buffer-live-p buf) buf (get-buffer-create "*scratch*"))
+       (if (buffer-live-p buf) buf (startup--get-buffer-create-scratch))
        'norecord)))
 
           ;; Delete the client if necessary.
diff --git a/lisp/simple.el b/lisp/simple.el
index 87e0b23377..e2d3cd1505 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -1555,12 +1555,12 @@ eval-expression
          (eval-expression-get-print-arguments current-prefix-arg)))
 
   (if (null eval-expression-debug-on-error)
-      (push (eval exp lexical-binding) values)
+      (push (eval exp t) values)
     (let ((old-value (make-symbol "t")) new-value)
       ;; Bind debug-on-error to something unique so that we can
       ;; detect when evalled code changes it.
       (let ((debug-on-error old-value))
- (push (eval (macroexpand-all exp) lexical-binding) values)
+ (push (eval (macroexpand-all exp) t) values)
  (setq new-value debug-on-error))
       ;; If evalled code has changed the value of debug-on-error,
       ;; propagate that change to the global binding.
diff --git a/lisp/startup.el b/lisp/startup.el
index 688ea84b7b..f3c0c7b100 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -2095,7 +2095,7 @@ normal-no-mouse-startup-screen
   (insert "\t\t")
   (insert-button "Open *scratch* buffer"
  'action (lambda (_button) (switch-to-buffer
-                                       (get-buffer-create "*scratch*")))
+                                       (startup--get-buffer-create-scratch)))
  'follow-link t)
   (insert "\n")
   (insert "\n" (emacs-version) "\n" emacs-copyright "\n")
@@ -2221,6 +2221,12 @@ display-about-screen
 (defalias 'about-emacs 'display-about-screen)
 (defalias 'display-splash-screen 'display-startup-screen)
 
+(defun startup--get-buffer-create-scratch ()
+  (with-current-buffer (get-buffer-create "*scratch*")
+    (set-buffer-major-mode (current-buffer))
+    (setq-local lexical-binding t)
+    (current-buffer)))
+
 (defun command-line-1 (args-left)
   "A subroutine of `command-line'."
   (display-startup-echo-area-message)
@@ -2485,7 +2491,7 @@ command-line-1
     (when (eq initial-buffer-choice t)
       ;; When `initial-buffer-choice' equals t make sure that *scratch*
       ;; exists.
-      (get-buffer-create "*scratch*"))
+      (startup--get-buffer-create-scratch))
 
     ;; If *scratch* exists and is empty, insert initial-scratch-message.
     ;; Do this before switching to *scratch* below to handle bug#9605.
@@ -2504,7 +2510,7 @@ command-line-1
    ((functionp initial-buffer-choice)
     (funcall initial-buffer-choice))
                    ((eq initial-buffer-choice t)
-                    (get-buffer-create "*scratch*"))
+                    (startup--get-buffer-create-scratch))
                    (t
                     (error "initial-buffer-choice must be a string, a function, or t.")))))
         (unless (buffer-live-p buf)
diff --git a/lisp/window.el b/lisp/window.el
index d7fdceb205..7f89c87af1 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -4578,7 +4578,7 @@ last-buffer
   (or (get-next-valid-buffer (nreverse (buffer-list frame))
      buffer visible-ok frame)
       (get-buffer "*scratch*")
-      (let ((scratch (get-buffer-create "*scratch*")))
+      (let ((scratch (startup--get-buffer-create-scratch)))
  (set-buffer-major-mode scratch)
  scratch)))
 



Reply | Threaded
Open this post in threaded view
|

Re: bug#30078: 27.0.50; Use lexical-binding for M-:

nazienham
In reply to this post by Stefan Monnier
Hello Madam and Mister.
We are lenders who offer loans between individual national and international.
You do not have the favor of the banks or you're looking for quick loan in 48 hours.
We offer loan if you are able to repay with our interest which is 4% per year, we offer from $ 1000 to $ 800000.
We do Hello Madam & Mr.
Loans:
Individual loan school ready group investment individual investment group thank you madam  & Sir for your understanding, for more information contact us by email: [hidden email]
Reply | Threaded
Open this post in threaded view
|

bug#30078: 27.0.50; Use lexical-binding for M-:

Glenn Morris-3
In reply to this post by Stefan Monnier
Stefan Monnier wrote:

> How 'bout this patch, then?

You forgot all the documentation updates. :)



Reply | Threaded
Open this post in threaded view
|

bug#30078: 27.0.50; Use lexical-binding for M-:

Michael Heerdegen
In reply to this post by Stefan Monnier
Stefan Monnier <[hidden email]> writes:

>  (defun command-line-1 (args-left)
>    "A subroutine of `command-line'."
>    (display-startup-echo-area-message)
> @@ -2485,7 +2491,7 @@ command-line-1
>      (when (eq initial-buffer-choice t)
>        ;; When `initial-buffer-choice' equals t make sure that *scratch*
>        ;; exists.
> -      (get-buffer-create "*scratch*"))
> +      (startup--get-buffer-create-scratch))
>  
>      ;; If *scratch* exists and is empty, insert initial-scratch-message.
>      ;; Do this before switching to *scratch* below to handle bug#9605.

Don't you also want to change this occurrence in `command-line'?

#+begin_src emacs-lisp
  ;; If *scratch* exists and init file didn't change its mode, initialize it.
  (if (get-buffer "*scratch*")
      (with-current-buffer "*scratch*"
        (if (eq major-mode 'fundamental-mode)
            (funcall initial-major-mode))))
#+end_src

So far, lexical-binding in *scratch* is nil with emacs -Q with your
patch.

Maybe also check that evaluating the expression `lexical-binding'
returns a result that is consistent with the evaluation behavior.  In
particular, evaluating lexical-binding in M-x ielm RET still gives nil -
that's confusing.


Michael.



Reply | Threaded
Open this post in threaded view
|

bug#30078: 27.0.50; Use lexical-binding for M-:

Eli Zaretskii
In reply to this post by Stefan Monnier
> From: Stefan Monnier <[hidden email]>
> Date: Fri, 12 Jan 2018 11:12:23 -0500
> Cc: [hidden email]
>
> +** Lexical binding is now used when evaluating interactive Elisp forms
> +More specifically, lexical-binding is used for M-:, M-x ielm, as well
> +as in the *scratch* buffer.

Please, not in *scratch*.  Setting variables and then evaluating forms
that use them is very useful there.



Reply | Threaded
Open this post in threaded view
|

bug#30078: 27.0.50; Use lexical-binding for M-:

Stefan Monnier
In reply to this post by Michael Heerdegen
> Don't you also want to change this occurrence in `command-line'?
>
> #+begin_src emacs-lisp
>   ;; If *scratch* exists and init file didn't change its mode, initialize it.
>   (if (get-buffer "*scratch*")
>       (with-current-buffer "*scratch*"
> (if (eq major-mode 'fundamental-mode)
>    (funcall initial-major-mode))))
> #+end_src

Ah, so that's where it is, thanks.

> So far, lexical-binding in *scratch* is nil with emacs -Q with your
> patch.

That's what I was seeing as well, indeed :-(

> Maybe also check that evaluating the expression `lexical-binding'
> returns a result that is consistent with the evaluation behavior.

I already know that the check will fail.  Is it important?

In general the value of the variable lexical-binding during evaluation
of the code is not directly related to whether the code is evaluated
using lexical rules or not.  Within macros, Emacs tries to make sure
that lexical-binding reflects whether the code returned by the macro
will use lexical scoping or not (so that the macro can adjust the code
it generates accordingly), but other than that, the two are
largely independent (which is why (setq lexical-binding t) is not the
right way to enable lexical scoping in a file).

> In particular, evaluating lexical-binding in M-x ielm RET still gives
> nil - that's confusing.

Right (well, it doesn't always give you nil: it depends on the value of
lexical-binding in the buffer currently selected to evaluate the IELM
expressions).
But this discrepancy also shows up in various other contexts.  Eg.:

    ;; -*- lexical-binding:t -*-
    (defun my-test ()
      (interactive)
      (message "%S" lexical-binding))

will give you a lexically scoped command which will tell you the value
of lexical-binding at the time&place you run the command, which will
sometimes be t and sometimes not.

I'm not sure we should try to encourage the user to think a piece of
code can check the value of `lexical-binding` to know if it's being
evaluated using lexical scoping rules.


        Stefan



Reply | Threaded
Open this post in threaded view
|

bug#30078: 27.0.50; Use lexical-binding for M-:

Stefan Monnier
In reply to this post by Eli Zaretskii
> Please, not in *scratch*.  Setting variables and then evaluating forms
> that use them is very useful there.

I'm not sure how the two sentences are connected:
Setting lexical-binding to t in *scratch* won't prevent setting
variables and evaluating forms that use them.


        Stefan



Reply | Threaded
Open this post in threaded view
|

bug#30078: 27.0.50; Use lexical-binding for M-:

Eli Zaretskii
> From: Stefan Monnier <[hidden email]>
> Cc: [hidden email], [hidden email]
> Date: Fri, 12 Jan 2018 13:49:00 -0500
>
> > Please, not in *scratch*.  Setting variables and then evaluating forms
> > that use them is very useful there.
>
> I'm not sure how the two sentences are connected:
> Setting lexical-binding to t in *scratch* won't prevent setting
> variables and evaluating forms that use them.

For some value of "prevent", surely?  Dynamically-bound variables will
no longer work as they did before, right?



Reply | Threaded
Open this post in threaded view
|

bug#30078: 27.0.50; Use lexical-binding for M-:

Stefan Monnier
>> > Please, not in *scratch*.  Setting variables and then evaluating forms
>> > that use them is very useful there.
>> I'm not sure how the two sentences are connected:
>> Setting lexical-binding to t in *scratch* won't prevent setting
>> variables and evaluating forms that use them.
> For some value of "prevent", surely?  Dynamically-bound variables will
> no longer work as they did before, right?

No, they'll work pretty much as before.


        Stefan



Reply | Threaded
Open this post in threaded view
|

bug#30078: 27.0.50; Use lexical-binding for M-:

Michael Heerdegen
In reply to this post by Stefan Monnier
Stefan Monnier <[hidden email]> writes:

> I'm not sure we should try to encourage the user to think a piece of
> code can check the value of `lexical-binding` to know if it's being
> evaluated using lexical scoping rules.

I'm not sure, too.  But aren't *scratch* and *ielm* special?  These are
the current buffers when code is evaluated, but they are also some kind
of source-code buffer at the same time.  In source code file buffers,
M-x lexical-binding RET gives you the right answer about how the code in
this buffers is interpreted.

I think we should be careful - maybe new users are trying to learn Elisp
with the help of *ielm*.

With your patch, you get

ELISP> (let ((x 1)) (lambda () x))
(closure
 ((x . 1)
  t)
 nil x)

but if you put point after the input expression and hit C-x C-e you
suddenly get

(lambda nil x)

People who are actually learning Elisp will not make any sense out of
this, I think.


Michael.



Reply | Threaded
Open this post in threaded view
|

bug#30078: 27.0.50; Use lexical-binding for M-:

Eli Zaretskii
In reply to this post by Stefan Monnier
> From: Stefan Monnier <[hidden email]>
> Cc: [hidden email], [hidden email]
> Date: Fri, 12 Jan 2018 14:06:41 -0500
>
> >> > Please, not in *scratch*.  Setting variables and then evaluating forms
> >> > that use them is very useful there.
> >> I'm not sure how the two sentences are connected:
> >> Setting lexical-binding to t in *scratch* won't prevent setting
> >> variables and evaluating forms that use them.
> > For some value of "prevent", surely?  Dynamically-bound variables will
> > no longer work as they did before, right?
>
> No, they'll work pretty much as before.

Then you are saying that turning on lexical-binding in *scratch* will
change nothing at all?  I very much doubt that, and my witness are all
those packages that broke due to lexical-binding and needed minor
fixes.



Reply | Threaded
Open this post in threaded view
|

bug#30078: 27.0.50; Use lexical-binding for M-:

Noam Postavsky-2
On Fri, Jan 12, 2018 at 3:03 PM, Eli Zaretskii <[hidden email]> wrote:

>> From: Stefan Monnier <[hidden email]>
>> Cc: [hidden email], [hidden email]
>> Date: Fri, 12 Jan 2018 14:06:41 -0500
>>
>> >> > Please, not in *scratch*.  Setting variables and then evaluating forms
>> >> > that use them is very useful there.
>> >> I'm not sure how the two sentences are connected:
>> >> Setting lexical-binding to t in *scratch* won't prevent setting
>> >> variables and evaluating forms that use them.
>> > For some value of "prevent", surely?  Dynamically-bound variables will
>> > no longer work as they did before, right?
>>
>> No, they'll work pretty much as before.
>
> Then you are saying that turning on lexical-binding in *scratch* will
> change nothing at all?  I very much doubt that, and my witness are all
> those packages that broke due to lexical-binding and needed minor
> fixes.

Top-level `setq's would be unaffected (because the lexical environment
at the top level is empty). Lexical binding only changes what `let'
does, for undeclared variables.
(okay, not *just* `let', it also affects `lambda')



Reply | Threaded
Open this post in threaded view
|

bug#30078: 27.0.50; Use lexical-binding for M-:

Michael Heerdegen
In reply to this post by Eli Zaretskii
Eli Zaretskii <[hidden email]> writes:

> Then you are saying that turning on lexical-binding in *scratch* will
> change nothing at all?  I very much doubt that, and my witness are
> all those packages that broke due to lexical-binding and needed minor
> fixes.

I do what has been proposed for months.  I liked it.

What are we doing with scratch?

(1) Setting global variables, defining small functions and such simple
stuff.  lexical-binding doesn't make a big difference here.

(2) Re-evaluate parts of the source code, often with small
modifications, for testing and debugging.  Since more and more packages
make active use of lexical-binding, this is actually broken currently!
Re-evaluating parts of source code in scratch can currently break Emacs.

(3) I often posted some code examples in emacs-help which made use of
lexical-binding.  People pasted it into scratch and it didn't work.
It's currently even not trivial to evaluate such examples with
lexical-binding on.

OTOH, there is not too much code that really relies on dynamical binding
mode.  And very often, such code is just written in a bad style (missing
`defvar's etc.).

In summary, I think the advantages clearly prevail.


Michael.



Reply | Threaded
Open this post in threaded view
|

bug#30078: 27.0.50; Use lexical-binding for M-:

Eli Zaretskii
> From: Michael Heerdegen <[hidden email]>
> Cc: Stefan Monnier <[hidden email]>,  [hidden email]
> Date: Fri, 12 Jan 2018 21:55:25 +0100
>
> What are we doing with scratch?
>
> (1) Setting global variables, defining small functions and such simple
> stuff.  lexical-binding doesn't make a big difference here.

We need to discuss the differences whether they are big or not.

> (2) Re-evaluate parts of the source code, often with small
> modifications, for testing and debugging.  Since more and more packages
> make active use of lexical-binding, this is actually broken currently!
> Re-evaluating parts of source code in scratch can currently break Emacs.

You are describing a mistake.  People make mistakes all the time, and
will continue making mistakes whatever we do.  That shouldn't be a
basis for our decisions.

> (3) I often posted some code examples in emacs-help which made use of
> lexical-binding.  People pasted it into scratch and it didn't work.
> It's currently even not trivial to evaluate such examples with
> lexical-binding on.

Another mistake.  I don't see why this should be of any importance for
the decision at hand.

> OTOH, there is not too much code that really relies on dynamical binding
> mode.  And very often, such code is just written in a bad style (missing
> `defvar's etc.).

That's your opinion.  I happen not to share it.

> In summary, I think the advantages clearly prevail.

I actually didn't yet see any advantages mentioned, and the above
aren't, IMO.  Lexical-binding is just a feature, albeit an important
one.  It doesn't have to be introduced into every possible corner of
Emacs, not without a good reason.



Reply | Threaded
Open this post in threaded view
|

bug#30078: 27.0.50; Use lexical-binding for M-:

Michael Heerdegen
Eli Zaretskii <[hidden email]> writes:

> I actually didn't yet see any advantages mentioned, and the above
> aren't, IMO.  Lexical-binding is just a feature, albeit an important
> one.  It doesn't have to be introduced into every possible corner of
> Emacs, not without a good reason.

Ah, so you doubt that lexical-binding once should just be the default.
Then I think we should discuss about this goal.


Michael.



Reply | Threaded
Open this post in threaded view
|

bug#30078: 27.0.50; Use lexical-binding for M-:

Luis Gerhorst
> On 12. Jan 2018, at 22:11, Michael Heerdegen <[hidden email]> wrote:
>
> Ah, so you doubt that lexical-binding once should just be the default.
> Then I think we should discuss about this goal.

Here are a few possible pros of lexical binding:

1) Make Elisp more approachable to newcomers. Lexical binding is the default in most programming languages and thus more people are familiar with it. This makes it easier for people to understand existing code since they don‘t have to get used to implicit dynamic binding first, which the code may take advantage of.

2) Make existing code more maintainable. I was involved in porting the package emacs-eclim to lexical binding. The process shed light on some very dark parts of the codebase nobody had touched in a while since they were written in very bad style and thus hard to understand. Using lexical binding forced us to simplify those parts.

3) This is a minor one: I‘ve heard that lexical binding allows for more optimizations. I don‘t know to which extend this actually happens and whether it is noticeable to the user.

I believe 1) and 2) will significantly help improving the general quality of Elisp code and thus of Emacs in general.

Here‘s the big con:

If lexical binding should become the default one day, you would have to give everyone a reasonable amount of time (multiple years?) to adapt for it and port their code or set lexical binding to nil in their files.

One idea would be to make elisp-mode somehow encourage users to enable lexical binding in the files they are editing. Any ideas how this can be done without annoying users? Ideas:
- Prompting every time elisp-mode is enabled in a file that does not have a lexical binding header.
- I know that certain functions insert a apropriate copyright notice into el-files, do / can those also set lexical binding to t?

Best regards,
Luis



Reply | Threaded
Open this post in threaded view
|

bug#30078: 27.0.50; Use lexical-binding for M-:

Stefan Monnier
In reply to this post by Michael Heerdegen
>> I'm not sure we should try to encourage the user to think a piece of
>> code can check the value of `lexical-binding` to know if it's being
>> evaluated using lexical scoping rules.
> I'm not sure, too.  But aren't *scratch* and *ielm* special?  These are
> the current buffers when code is evaluated,

For *scratch* yes, but for *ielm* that's not the case: the code is
evaluated in the `ielm-working-buffer` (whose name normally appears in
the mode-line after "IELM").


        Stefan



12