help-gnu-emacs
[Top][All Lists]
Advanced

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

Re: How to create a higher order function?


From: Stefan Monnier
Subject: Re: How to create a higher order function?
Date: Wed, 22 Sep 2021 15:03:42 -0400
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (gnu/linux)

> (defun negate (fun)
>   "Return a function returning the logical opposite of FUN."
>   `(lambda (&rest args)
>     (not (apply ,(symbol-function fun) args))))

This will fail when `fun` is not a function name but a (lambda ...).
It will also fail when `fun` is a function alias (hence (symbol-function
fun) returns the name of another function).
It will also fail when `fun` is a symbol whose content is an interpreted
lexically scoped function (hence (symbol-function fun) returns
something of the form (closure ...)).
It will also fail when `fun` is an autoloaded function that's not yet
loaded (hence (symbol-function fun) returns something of the form
(autoload ...)).

I think what you meant was

    (defun negate (fun)
      "Return a function returning the logical opposite of FUN."
      `(lambda (&rest args)
        (not (apply ',fun args))))

> But maybe (B) is better under some circumstances?

I'm hoping B can be banned at some point in the future ;-)

> Is it faster?

I don't think so.  Currently closures constructed in lexical-binding
mode are not super efficient (back in Emacs-24, the focus was on
providing lexical-scoping with clean semantics and without breaking or
slowing down dynamic scoping.  Efficiency of lexical-binding was
secondary, and we haven't really attacked that yet).

More specifically, constructing a closure will allocate two new vectors
(whereas the ideal would be 1 vector), and one of them holds a lot more
than the set of free variables (it also holds all the constants used in
the code, such as the names of all the functions it calls).

But the above construction of a (lambda ...) list to mimic a true
closure isn't very efficient either: if I count correctly it will need
to allocate 9 new cons cells.

Which is currently faster, I don't know.  But if the construction of the
true closure is slower, at least there's the theoretical possibility to
invest implementation efforts into making it more efficient by using
a more conventional closure representation.

As for efficiency when calling the constructed function, I'm pretty sure
that C will be significantly faster (tho you'll have to call them many
times to see the difference ;-), but I suspect that in practice for this
specific example the difference may not be very large because most of
the time will be spent processing the `&rest` (i.e. converting the args
(stored in a vector) into a list) and the `apply` (i.e. converting the
list back to a vector), and the benchmark will likely show that you're
spending a lot of time in the GC (because of the need to recover all
those temporary lists generated by the `&rest`).

So to better see the effect you might want to use something like:

    (defun negate (fun)
      "Return a function returning the logical opposite of FUN."
      (lambda (arg1 arg2)
        (not (funcall fun arg1 arg2))))

where I'd expect the (C) version to be faster by a quite
significant margin (like more than a factor 2).


        Stefan




reply via email to

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