unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Daniel Colascione <dan.colascione@gmail.com>
To: Stefan Monnier <monnier@iro.umontreal.ca>
Cc: emacs-devel@gnu.org
Subject: Re: lexbind ready for merge
Date: Tue, 29 Mar 2011 21:10:19 -0700	[thread overview]
Message-ID: <4D92AD2B.40502@gmail.com> (raw)
In-Reply-To: <jwvsju55sqs.fsf-monnier+emacs@gnu.org>

On 3/29/2011 6:22 PM, Stefan Monnier wrote:
>> - apply-partially should have a compiler-macro now that we can implement it
>> very efficiently; also, funcall-partially.
>
> The current definition of apply-partially is not too
> inefficient, actually.  I don't know what funcall-partially would
> be like.  And I don't like either of them: apply-partially is just a way
> to make it easy to build closures by hand without resorting to
> lexical-let and independently from lexical-binding, but once you use
> lexical-binding you're usually better off using a plain closure.

apply-partially at least involves less typing lexical-let, which (as I 
explain below) is required in some cases even with lexbind.

As for funcall-partially: I never really liked how apply-partially 
differs from apply.  The latter treats its last argument specially, 
while the former does not.  If I had my way, I'd rename the current 
apply-partially to funcall-partially (or partial-funcall?) and create a 
new apply-partially that unpacks its last argument.  But it probably 
can't be changed now...

>> - It might be a good idea to remove the "Once Emacs 19 becomes standard..."
>> comment from cl.el
>
> Feel free to do that on the trunk, I don't think it's really related
> to lexbind.

If I could, I would. :-)

>> - Can lexical-let and lexical-let* be made a no-op when compiling lexbound
>> code?  Looking at cl.el, it appears they're still up their usual
>> dirty tricks.
>
> They can *almost* be turned into `let' and `let*', except that
> (lexical-let ((buffer-file-name 3)) ...) will bind buffer-file-name
> lexically whereas `let' will always bind it dynamically.  We could
> either ignore those issues or try to handle them, but I'd rather just
> mark lexical-let obsolete.

I'd prefer to ignore the issues for now and transform lexical-let to let 
when lexical-binding is on, generating a compiler error if we're trying 
to lexically bind a special variable.  I don't think many people try to 
do that.  A macro that uses a lexical binding in its generated code 
still needs to use lexical-let in order for its generated form to work 
properly in either environment, and even outside macros, making 
lexical-let cheap in the lexbound case gives us a way to create 
backward-compatible code that automatically becomes more efficient in 
Emacs 24.

> (Of course, there's also the difficulty for the macro to reliably
> determine whether the expansion will be run in lexical-binding or
> dynamic-binding mode).

Wouldn't inspecting the value of lexical-binding work well enough?  For 
evaluated code, the macro (regardless of how or whether it was compiled) 
will be called directly from the evaluator with the appropriate value of 
lexical-binding set.  For compiled code, the macro will be called by the 
compiler with lexical-binding set to the value the compiler is using to 
compile the resulting form.

The value of lexical-let at macro-run time and result-evaluation time 
*could* differ if the resulting form is squirreled away somewhere and 
reused later --- but I can't think of a case where that happens without 
the code being wrapped in some kind of function object that would 
capture the current value of lexical-let.

Of course, you can lie:

(defmacro* with-broken-code (&body body)
   (let (lexical-binding)
     (macroexpand-all `(progn ,@body)))

That kind of thing is firmly in "It hurts when I do this" territory though.

>
>> - lexical-binding only applies to code evaluated by `eval-buffer' and
>> eval-region'?! So I can't make code evaluated by M-: lexbound?
>
> ?!?  AFAIK, M-: uses lexical or dynamic scoping according to the value
> of lexical-binding in the current buffer.

It does, but the documentation string still gives me the impression that 
it wouldn't be.  Why isn't lexical-binding respected for all evaluation?

>> - It'd be nice to be able to write small pieces of lexical code in
>> non-lexbound code, e.g., in the expansion of a macro that gets used by both
>> lexbound and non-lexbound.
>
> Yes, it'd be nice.
>
>> What's the best way to do that?
>
> Currently, I think the best way to do that is to add the feature to the
> byte-compiler.  The most promising avenue for it might be to use code
> of the form ((closure ()<formalargs>  <lexbindcode>)<actualargs>) and
> compile efficiently (I think currently such code will either result in
> a complaint about a malformed function, or else will leave the function
> uncompiled).

Ah, I see what you mean.  Would a with-lexical-scope form suffice?

;; with-lexical-scope is itself compiled with lexical-binding t
(defmacro* with-lexical-scope (&body body)
   (let* ((vars (remove-if (lambda (var)
                             (or (special-variable-p var)
                                 (not (boundp var))))
                           (find-free-vars `(progn ,@body))))
          (closure `(closure (t) ; no environment
                   ,@args ,@body)))
     `(funcall ,closure ,@vars)))

(defvar b 42)
(defun some-dynamic-function (a)
   (with-lexical-scope
     (lambda (c)
       (+ a b c)))

;; or even

(with-lexical-scope
   (defun foo (arg) ...))

That's not the same as (defun foo (arg) (with-lexical-scope ...))) 
because the argument binding strategies differ.

>> - The documentation claims that defun doesn't capture its lexical scope. In
>> interpreted code, it does.
>
>> (require 'cl)
>> (let ((bar2 5))
>>    (defun foo ()
>>      (incf bar2)
>>      (message "hi: %s" bar2)))
>
>> In compiled code, we do not capture the variable and instead warn about it.
>> Instead, we should capture the variable.
>
> Yup.
>
>> Common Lisp explicitly allows this use, and it's convenient in
>> some cases.
>
> If you need it you can use
>
>    (let ((bar2 5))
>      (defalias 'foo (lambda ()
>        "Foo."
>        (incf bar2)
>        (message "hi: %s" bar2))))
>
> IIRC, the reason why defun doesn't work for it is fundamentally linked
> to some silly technicality, but I justify it for myself by the fact that
> all the "defun within a non-empty context" I've seen were bogus, so I'm
> not strongly motivated to handle it right.

Even if it's not particularly common, being consistent with Common Lisp 
and having fewer special cases are good things.  Some people use 
constructs like this to create module-private variables (which is a bad 
idea, but that doesn't stop people doing it.)

>> - Disassembling a closure reports closed-over variables as constants;
>> they're not.
>
> They are.

Err, yes.  They are.

>> - Do we really use a whole cons cell for each closed-over variable, even in
>> compiled code?
>
> Yes, tho only for variables which are mutated (yup, `setq' is costly).

Couldn't we create a box type, which would would be like a cons except 
that it'd hold just one value?  (The #<foo> read-syntax is available.)



  reply	other threads:[~2011-03-30  4:10 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-03-29 21:44 lexbind ready for merge Stefan Monnier
2011-03-29 23:43 ` Daniel Colascione
2011-03-30  1:22   ` Stefan Monnier
2011-03-30  4:10     ` Daniel Colascione [this message]
2011-03-30 11:35       ` Juanma Barranquero
2011-03-30 13:24         ` lexbind: how to replace lexical-let approach to hide secrets (was: lexbind ready for merge) Ted Zlatanov
2011-03-30 21:12           ` lexbind: how to replace lexical-let approach to hide secrets Stefan Monnier
2011-03-30 21:56             ` David Kastrup
2011-03-30 22:29               ` Daniel Colascione
2011-03-31 15:42             ` Ted Zlatanov
2011-04-01  1:31               ` Stephen J. Turnbull
2011-04-01  4:41                 ` secret strings (was: lexbind: how to replace lexical-let approach to hide secrets) Ted Zlatanov
2011-04-01  5:52                   ` Stephen J. Turnbull
2011-04-01 11:02                     ` secret strings Ted Zlatanov
2011-04-01 14:38                       ` Stephen J. Turnbull
2011-04-01 15:12                         ` Ted Zlatanov
2011-04-01 16:14                           ` Stephen J. Turnbull
2011-04-01 20:08                             ` Ted Zlatanov
2011-04-01 20:34                               ` Stefan Monnier
2011-04-01 21:25                                 ` Ted Zlatanov
2011-04-01 14:59                       ` Stefan Monnier
2011-03-30 15:09         ` lexbind ready for merge Daniel Colascione
2011-03-30 15:20           ` Juanma Barranquero
2011-03-30 14:26       ` Stefan Monnier
2011-03-30  7:28 ` Tassilo Horn
2011-03-30 11:30   ` Eli Zaretskii
2011-03-30 13:10     ` Tassilo Horn
2011-03-30 13:17     ` Ted Zlatanov
2011-03-30 14:29   ` Stefan Monnier
2011-03-30 14:54     ` Tassilo Horn
2011-03-31  1:02       ` Stephen J. Turnbull
2011-03-30 16:11     ` Lars Magne Ingebrigtsen
2011-03-30 17:10       ` Tassilo Horn

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=4D92AD2B.40502@gmail.com \
    --to=dan.colascione@gmail.com \
    --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).