[Top][All Lists]

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

Re: Can the byte-compiler check whether functions passed by name are def

From: Klaus-Dieter Bauer
Subject: Re: Can the byte-compiler check whether functions passed by name are defined?
Date: Sun, 4 Aug 2013 20:41:13 +0200

2013/8/1 Stefan Monnier <address@hidden>
>>> 1. Has to be done, though maybe through a macro, for every higher-order
>>> function.
>> Note that macroexp.el already has special handling for the main
>> higher-order functions (to warn about '(lambda ...)).  So it could be
>> implemented there.
> I presume the list of “main higher-order” functions is hard-coded
> then, isn't it?

Currently, yes.  It could be moved from macroexp.el's main loop to
a bunch of compiler-macros (the code predates the introduction of
compiler macros in core Elisp), if needed.

> Is there a symbol property or "declare" form to mark specific
> arguments as function arguments, to have the same warnings for higher
> order functions from 3rd party libraries, too?  Similar to how
> "docstring" arguments for macros are handled?

No, and I think it would be overkill.  But 3rd party can also use
a compiler-macro to turn the ' into a #'.

The approach I use for '(lambda ...) is to emit warnings in the common
cases, so that coders will slowly learn, which then benefits all cases.

For '(lambda ...) that works well, because removing the ' (or using #')
is often useful (making it possible to byte-compile the code, or using
lexically bound vars), but for single quoted symbols, the benefit is
a lot less clear ("turn 'foo into #'foo to get rid of the silly new
warning" only to then get another warning because the compiler doesn't
understand that `foo' will very much exist by the time we try to call it).


 I have written an implementation of the compile-time check, see the attached patch to "lisp/bytecomp.el". Since I also introduced as new declare form for `defun', I have also attached a patch to "doc/functions.texi".

The code handles both the cases (function SYMBOL) and (quote SYMBOL). In the latter case it requires a symbol property "higher-order-arguments" to be set, which should preferably be done by a declare form, but for now is implemented by checking for a set of common functions (apply, funcall, all functions I found starting with "map" or "cl-map") for the property and setting it if it is not yet set. The value of the property should be a list of integers, which give the positions of the arguments that are expected to be functions, counting from 0.

If the patch becomes part of the emacs sources, I can also provide a patch that adds the needed declare forms to the various definitions, though I don't know how to do it for functions defined in the C-code such as `mapcar'.

Currently such a function (in this case requiring lexical binding) definition would read

(defun my-combine (func1 func2)
(declare (higher-order-arguments 0 1)
(lambda (arg)
(funcall func1 (funcall func2 arg))))


(defun my-map (func list)
(declare (higher-order 0))
(mapcar func list))

I was thinking of allowing to declare the number of arguments the function will be passed, but didn't have the time for that detail (yet?).

One thing I am not sure about: I define the handler for the declare form in "bytecomp.el", hence the handler is not known before loading that file. When loading a file from source the declare form will therefore cause a warning. When compiling it doesn't seem to cause issues though.

I couldn't define the handler in "byte-run.el" however, as when I added it to the declaration of `defun-declaration-alist', it would suddenly be missing again during compilation.

- Klaus

Attachment: functions.texi.patch
Description: Binary data

Attachment: bytecomp.el.patch
Description: Binary data

reply via email to

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