unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Arbitrary function: find the number(s) of expected arguments
@ 2016-03-15 18:48 Paul Pogonyshev
  2016-03-15 22:45 ` Davis Herring
  2016-03-16  3:47 ` Stefan Monnier
  0 siblings, 2 replies; 60+ messages in thread
From: Paul Pogonyshev @ 2016-03-15 18:48 UTC (permalink / raw)
  To: emacs-devel

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

Hi,

Suppose I have a library with

    (defun my-foo (callback)
      ...
      (funcall callback computed-data))

Now, _some_ users of my library found out that it would be nice to have
`this-value-gets-computed-as-side-effect' too, though it is not really
necessary for callbacks in general. I wouldn't want to change interface of
the library, instead I would just improve it like this:

    (defun my-foo (callback)
      ...
      (if (accepts-2-arguments callback)
          (funcall callback computed-data
this-value-gets-computed-as-side-effect)
        (funcall callback computed-data)))

This way `my-foo' automagically accepts callbacks that expect one argument
(as in initial form), as well as those that want two arguments.

The problem, of course, is to write `accepts-2-arguments'.  I found
`subr-arity', which provides exactly what I need, but only for builtins...
I was also told about `help-function-arglist', but it returns the raw
argument list in which you still have to handle &optional or &rest (maybe
something else too?). This looks like a bit too much work for simple thing
and likely not good for performance-critical places either.

Feature request: add a builtin like `subr-arity' that works for _any_
function. Evaluator sure knows how to call the functions, so the
information is already there.

Paul

[-- Attachment #2: Type: text/html, Size: 1677 bytes --]

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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-15 18:48 Arbitrary function: find the number(s) of expected arguments Paul Pogonyshev
@ 2016-03-15 22:45 ` Davis Herring
  2016-03-16  7:41   ` Paul Pogonyshev
  2016-03-16  3:47 ` Stefan Monnier
  1 sibling, 1 reply; 60+ messages in thread
From: Davis Herring @ 2016-03-15 22:45 UTC (permalink / raw)
  To: Paul Pogonyshev, emacs-devel

> This way `my-foo' automagically accepts callbacks that expect one argument
> (as in initial form), as well as those that want two arguments.

What if your existing clients might have

(defun paul-callback (computed-data &optional cached)
   ...)

where they make calls to `paul-callback' themselves with the extra 
argument?  It's fair to call this "abstruse" (maybe most/all clients 
just use a lambda), but it might also make you think about providing 
another callback interface.

Davis

-- 
This product is sold by volume, not by mass.  If it appears too dense or 
too sparse, it is because mass-energy conversion has occurred during 
shipping.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-15 18:48 Arbitrary function: find the number(s) of expected arguments Paul Pogonyshev
  2016-03-15 22:45 ` Davis Herring
@ 2016-03-16  3:47 ` Stefan Monnier
  1 sibling, 0 replies; 60+ messages in thread
From: Stefan Monnier @ 2016-03-16  3:47 UTC (permalink / raw)
  To: emacs-devel

>       (if (accepts-2-arguments callback)
>           (funcall callback computed-data
> this-value-gets-computed-as-side-effect)
>         (funcall callback computed-data)))

    (condition-case nil
        (funcall callback computed-data this-value-gets-computed-as-side-effect)
      (wrong-number-of-arguments (funcall callback computed-data)))


-- Stefan




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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-15 22:45 ` Davis Herring
@ 2016-03-16  7:41   ` Paul Pogonyshev
  2016-03-19 12:26     ` Paul Pogonyshev
  0 siblings, 1 reply; 60+ messages in thread
From: Paul Pogonyshev @ 2016-03-16  7:41 UTC (permalink / raw)
  To: Davis Herring; +Cc: emacs-devel

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

That's a fair point, but frankly, it was just an example of why one might
want to use such a function to find arity of arbitrary callable. In this
example I could also invoke callback with two arguments only if it wants
_at least_ two. I.e.

    (my-foo (lambda (x &optional y) ...)) ==> called back with one argument

but

    (my-foo (lambda (x y) ...)) ==> called back with two arguments

Another usecase is possibility to fail early. I know that's not a popular
concept in Elisp, but I find it very useful. The idea is, when you are
given some value that you use only later, you validate it right away. Thus,
you can give immediate error with explanation rather than letting it fail
in mysterious and hard to understand way in a completely different place
later:

    (defun my-set-termination-callback (callback)
      (unless (and (functionp callback) (>= (car (function-arity callback))
1))
        (error ...))
      (setq my-termination-callback callback))

Paul

On 15 March 2016 at 23:45, Davis Herring <herring@lanl.gov> wrote:

> This way `my-foo' automagically accepts callbacks that expect one argument
>> (as in initial form), as well as those that want two arguments.
>>
>
> What if your existing clients might have
>
> (defun paul-callback (computed-data &optional cached)
>   ...)
>
> where they make calls to `paul-callback' themselves with the extra
> argument?  It's fair to call this "abstruse" (maybe most/all clients just
> use a lambda), but it might also make you think about providing another
> callback interface.
>
> Davis
>
> --
> This product is sold by volume, not by mass.  If it appears too dense or
> too sparse, it is because mass-energy conversion has occurred during
> shipping.
>

[-- Attachment #2: Type: text/html, Size: 2498 bytes --]

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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-16  7:41   ` Paul Pogonyshev
@ 2016-03-19 12:26     ` Paul Pogonyshev
  2016-03-19 13:10       ` Eli Zaretskii
  2016-03-19 14:26       ` Philipp Stephani
  0 siblings, 2 replies; 60+ messages in thread
From: Paul Pogonyshev @ 2016-03-19 12:26 UTC (permalink / raw)
  To: Davis Herring; +Cc: emacs-devel

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

Let's also consider it like this: `subr-arity' exists. But it has extremely
narrow and non-obvious field of use. Compare:

    (subr-arity (symbol-function 'car))  =>  (1 . 1)
    (subr-arity (symbol-function 'caar))  =>  eval: Wrong type argument:
subrp, #[257 "‰@@‡" [] 2 2409164]

And to make it even weirder (from `dash.el'):

    (defalias '-first-item 'car
      "Return the first item of LIST, or nil on an empty list.")

    (subr-arity (symbol-function '-first-item))  =>  (wrong-type-argument
subrp car)

What if I use `subr-arity' for whatever reason in a case where it works,
but in the next version Emacs moves the function from `.c' to `.el' and it
ceases to be a built-in?

Paul
​

[-- Attachment #2: Type: text/html, Size: 977 bytes --]

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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 12:26     ` Paul Pogonyshev
@ 2016-03-19 13:10       ` Eli Zaretskii
  2016-03-19 13:42         ` Paul Pogonyshev
                           ` (2 more replies)
  2016-03-19 14:26       ` Philipp Stephani
  1 sibling, 3 replies; 60+ messages in thread
From: Eli Zaretskii @ 2016-03-19 13:10 UTC (permalink / raw)
  To: Paul Pogonyshev; +Cc: emacs-devel

> Date: Sat, 19 Mar 2016 13:26:47 +0100
> From: Paul Pogonyshev <pogonyshev@gmail.com>
> Cc: emacs-devel@gnu.org
> 
> What if I use `subr-arity' for whatever reason in a case where it works, but in the next version Emacs moves
> the function from `.c' to `.el' and it ceases to be a built-in?

You need to guard the calls to subr-arity with subrp, and then your
code will be future-proof.

Patches to add func-arity to Emacs are welcome.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 13:10       ` Eli Zaretskii
@ 2016-03-19 13:42         ` Paul Pogonyshev
  2016-03-19 13:54         ` Michael Heerdegen
  2016-03-19 19:52         ` Stefan Monnier
  2 siblings, 0 replies; 60+ messages in thread
From: Paul Pogonyshev @ 2016-03-19 13:42 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

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

>
> You need to guard the calls to subr-arity with subrp, and then your
> code will be future-proof.
>

Eh, not really. Even if I catch the signal, it will still stop working in
the way it did before.

Patches to add func-arity to Emacs are welcome.
>

Ok, I'll see if I can do it.

Paul

[-- Attachment #2: Type: text/html, Size: 816 bytes --]

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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 13:10       ` Eli Zaretskii
  2016-03-19 13:42         ` Paul Pogonyshev
@ 2016-03-19 13:54         ` Michael Heerdegen
  2016-03-19 14:08           ` Eli Zaretskii
  2016-03-19 19:52         ` Stefan Monnier
  2 siblings, 1 reply; 60+ messages in thread
From: Michael Heerdegen @ 2016-03-19 13:54 UTC (permalink / raw)
  To: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> Patches to add func-arity to Emacs are welcome.

Question: Is this a reasonable way to go?  Will `func-arity' be a good
solution for the issues we discussed here?

For example, if

(defun f (a b) (list a b))

(defalias 'g (apply-partially #'f 1))

what would (func-arity 'g) return?  Would that be useful?

What would it return for adviced functions?  An advice can change the
arity of a function.  Most do not, but most advices have an &rest args
signature.  Any function can be adviced.

My question is if it is a good idea to invite users to rely on something
like `func-arity'.


Michael.




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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 13:54         ` Michael Heerdegen
@ 2016-03-19 14:08           ` Eli Zaretskii
  2016-03-19 15:20             ` Michael Heerdegen
  0 siblings, 1 reply; 60+ messages in thread
From: Eli Zaretskii @ 2016-03-19 14:08 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: emacs-devel

> From: Michael Heerdegen <michael_heerdegen@web.de>
> Date: Sat, 19 Mar 2016 14:54:10 +0100
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > Patches to add func-arity to Emacs are welcome.
> 
> Question: Is this a reasonable way to go?  Will `func-arity' be a good
> solution for the issues we discussed here?

Do you have a better solution to suggest?  If so, let's hear it.

> For example, if
> 
> (defun f (a b) (list a b))
> 
> (defalias 'g (apply-partially #'f 1))
> 
> what would (func-arity 'g) return?

Ideally, it should return (1 . 1).

> Would that be useful?

Why wouldn't it be?

> What would it return for adviced functions?  An advice can change the
> arity of a function.  Most do not, but most advices have an &rest args
> signature.  Any function can be adviced.

If the solution handles this complication, then it will return an
accurate result.  If not, it will be a known limitation.

> My question is if it is a good idea to invite users to rely on something
> like `func-arity'.

You could ask the same about subr-arity, couldn't you?  And yet we do
have it.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 12:26     ` Paul Pogonyshev
  2016-03-19 13:10       ` Eli Zaretskii
@ 2016-03-19 14:26       ` Philipp Stephani
  2016-03-19 16:51         ` Paul Pogonyshev
  1 sibling, 1 reply; 60+ messages in thread
From: Philipp Stephani @ 2016-03-19 14:26 UTC (permalink / raw)
  To: Paul Pogonyshev, Davis Herring; +Cc: emacs-devel

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

Paul Pogonyshev <pogonyshev@gmail.com> schrieb am Sa., 19. März 2016 um
13:26 Uhr:

> And to make it even weirder (from `dash.el'):
>
>     (defalias '-first-item 'car
>       "Return the first item of LIST, or nil on an empty list.")
>
>     (subr-arity (symbol-function '-first-item))  =>  (wrong-type-argument
> subrp car)
>

This at least can be made to work by using indirect-function instead of
symbol-function.

[-- Attachment #2: Type: text/html, Size: 808 bytes --]

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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 14:08           ` Eli Zaretskii
@ 2016-03-19 15:20             ` Michael Heerdegen
  2016-03-19 15:43               ` Eli Zaretskii
  0 siblings, 1 reply; 60+ messages in thread
From: Michael Heerdegen @ 2016-03-19 15:20 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> Do you have a better solution to suggest?  If so, let's hear it.

Not really.  The better solution would be that people should try to
avoid that situation.  This is not always possible, yes, but introducing
`fun-arity' would give a false appearance semblance reliability.

For example:

> > (defun f (a b) (list a b))
> > 
> > (defalias 'g (apply-partially #'f 1))
> > 
> > what would (func-arity 'g) return?
>
> Ideally, it should return (1 . 1).

This signature can be interpreted as "accepts any number of arguments",
whereby it doesn't.  The condition-case solution with the wrong number
of args handler at least also catches this case.

The problem with `func-arity' is that, as in the above example, can
return something that doesn't answer the question we talk about: "can
this function be called with these number of elements (e.g.)".  So it
only solves your problem if you are lucky (lucky at runtime).

> > What would it return for adviced functions?  An advice can change the
> > arity of a function.  Most do not, but most advices have an &rest args
> > signature.  Any function can be adviced.
>
> If the solution handles this complication, then it will return an
> accurate result.  If not, it will be a known limitation.

That's the problem: we can't handle this generally, since it can't be
known if and how an arbitrary advice calls the original funciton.

So after calling `func-arity' you still can't be sure whether a call
with a certain number of args or not.  That's not helpful.

> > My question is if it is a good idea to invite users to rely on
> > something like `func-arity'.
>
> You could ask the same about subr-arity, couldn't you?  And yet we do
> have it.

A subr is constant.  If you wrap it into another function, the result is
not a subr anymore.  So the result is at least more meaningful.


Hmm, A bit more reliable maybe (didn't think about it) would be
something semantically similar to

(condition-case nil
  (funcall function arguments ....)

 (wrong-number-of-arguments alternative-code ...))

that would catch "wrong-number-of-arguments" only at "top level" (which
would include calling the original function when FUNCTION is adviced,
but not function calls in the body of FUNCTION).


Michael.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 15:20             ` Michael Heerdegen
@ 2016-03-19 15:43               ` Eli Zaretskii
  2016-03-19 15:57                 ` Michael Heerdegen
                                   ` (2 more replies)
  0 siblings, 3 replies; 60+ messages in thread
From: Eli Zaretskii @ 2016-03-19 15:43 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: emacs-devel

> From: Michael Heerdegen <michael_heerdegen@web.de>
> Cc: emacs-devel@gnu.org
> Date: Sat, 19 Mar 2016 16:20:41 +0100
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > Do you have a better solution to suggest?  If so, let's hear it.
> 
> Not really.  The better solution would be that people should try to
> avoid that situation.  This is not always possible, yes, but introducing
> `fun-arity' would give a false appearance semblance reliability.

It will only do that if people don't quite understand what they are
doing.  I don't think we should assume our users will do that.

> For example:
> 
> > > (defun f (a b) (list a b))
> > > 
> > > (defalias 'g (apply-partially #'f 1))
> > > 
> > > what would (func-arity 'g) return?
> >
> > Ideally, it should return (1 . 1).
> 
> This signature can be interpreted as "accepts any number of arguments",
> whereby it doesn't.  The condition-case solution with the wrong number
> of args handler at least also catches this case.

But Emacs itself clearly _knows_ that only one argument is acceptable.
So a function that replicates the steps made by the Lisp interpreter
to arrive at this conclusion will be able to reach the same
conclusion.  So I don't see any insoluble problems here.

> The problem with `func-arity' is that, as in the above example, can
> return something that doesn't answer the question we talk about: "can
> this function be called with these number of elements (e.g.)".  So it
> only solves your problem if you are lucky (lucky at runtime).

I don't see how you reach this conclusion, except if you consider
faulty implementations.

> > > What would it return for adviced functions?  An advice can change the
> > > arity of a function.  Most do not, but most advices have an &rest args
> > > signature.  Any function can be adviced.
> >
> > If the solution handles this complication, then it will return an
> > accurate result.  If not, it will be a known limitation.
> 
> That's the problem: we can't handle this generally, since it can't be
> known if and how an arbitrary advice calls the original funciton.

When an advice has been installed, that is already known, isn't it?

Once again, how does Emacs know?

> > > My question is if it is a good idea to invite users to rely on
> > > something like `func-arity'.
> >
> > You could ask the same about subr-arity, couldn't you?  And yet we do
> > have it.
> 
> A subr is constant.  If you wrap it into another function, the result is
> not a subr anymore.  So the result is at least more meaningful.

But it covers only a part of the turf.  So nothing bad will happen if
we enlarge the turf a bit, right?

> Hmm, A bit more reliable maybe (didn't think about it) would be
> something semantically similar to
> 
> (condition-case nil
>   (funcall function arguments ....)
> 
>  (wrong-number-of-arguments alternative-code ...))
> 
> that would catch "wrong-number-of-arguments" only at "top level" (which
> would include calling the original function when FUNCTION is adviced,
> but not function calls in the body of FUNCTION).

Triggering an exception only answers a yes/no question, which is not
necessarily what is needed.  It is also a terribly inelegant solution,
IMO.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 15:43               ` Eli Zaretskii
@ 2016-03-19 15:57                 ` Michael Heerdegen
  2016-03-19 16:24                   ` Eli Zaretskii
  2016-03-19 16:14                 ` Philipp Stephani
  2016-04-18 23:02                 ` Davis Herring
  2 siblings, 1 reply; 60+ messages in thread
From: Michael Heerdegen @ 2016-03-19 15:57 UTC (permalink / raw)
  To: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> > > > (defun f (a b) (list a b))
> > > > 
> > > > (defalias 'g (apply-partially #'f 1))
> > > > 
> > > > what would (func-arity 'g) return?
> > >
> > > Ideally, it should return (1 . 1).
> > 
> > This signature can be interpreted as "accepts any number of arguments",
> > whereby it doesn't.  The condition-case solution with the wrong number
> > of args handler at least also catches this case.
>
> But Emacs itself clearly _knows_ that only one argument is acceptable.
> So a function that replicates the steps made by the Lisp interpreter
> to arrive at this conclusion will be able to reach the same
> conclusion.  So I don't see any insoluble problems here.

My examples where just special examples.  Generally, there is no clear
border or distinction between some kind of "wrapper" around a function
(like apply-partially or advice-add create), or a function that calls
another function in it's body.

So what should `function-arity' return?  The actual arity of the
function?  An arity that takes information about some known kinds of
wrappers into account?

No matter how you do it: the result will never give you a clear answer.
Even if the user understands what he is doing: if `function-arity' can't
answer the question whether a certain number of arguments is allowed, it
is of not much value.


Michael.




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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 15:43               ` Eli Zaretskii
  2016-03-19 15:57                 ` Michael Heerdegen
@ 2016-03-19 16:14                 ` Philipp Stephani
  2016-03-19 16:27                   ` Michael Heerdegen
  2016-03-19 16:27                   ` Eli Zaretskii
  2016-04-18 23:02                 ` Davis Herring
  2 siblings, 2 replies; 60+ messages in thread
From: Philipp Stephani @ 2016-03-19 16:14 UTC (permalink / raw)
  To: Eli Zaretskii, Michael Heerdegen; +Cc: emacs-devel

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

Eli Zaretskii <eliz@gnu.org> schrieb am Sa., 19. März 2016 um 16:44 Uhr:

> > For example:
> >
> > > > (defun f (a b) (list a b))
> > > >
> > > > (defalias 'g (apply-partially #'f 1))
> > > >
> > > > what would (func-arity 'g) return?
> > >
> > > Ideally, it should return (1 . 1).
> >
> > This signature can be interpreted as "accepts any number of arguments",
> > whereby it doesn't.  The condition-case solution with the wrong number
> > of args handler at least also catches this case.
>
> But Emacs itself clearly _knows_ that only one argument is acceptable.
> So a function that replicates the steps made by the Lisp interpreter
> to arrive at this conclusion will be able to reach the same
> conclusion.  So I don't see any insoluble problems here.
>

Knowing the arity in cases like this requires either evaluating the
function, or complex parsing of its definition. It is very reasonable to
assume that (func-arity 'g) would return (0 . many), because that is g's
arity as far as the Lisp interpreter is concerned.

[-- Attachment #2: Type: text/html, Size: 1461 bytes --]

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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 15:57                 ` Michael Heerdegen
@ 2016-03-19 16:24                   ` Eli Zaretskii
  2016-03-19 17:43                     ` Michael Heerdegen
  0 siblings, 1 reply; 60+ messages in thread
From: Eli Zaretskii @ 2016-03-19 16:24 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: emacs-devel

> From: Michael Heerdegen <michael_heerdegen@web.de>
> Date: Sat, 19 Mar 2016 16:57:59 +0100
> 
> So what should `function-arity' return?  The actual arity of the
> function?  An arity that takes information about some known kinds of
> wrappers into account?

It should returns information about the number of arguments with which
the function can be called right after the call to function-arity.

> No matter how you do it: the result will never give you a clear answer.

I disagree with this conclusion.  The fact is that the Emacs Lisp
interpreter does know how to produce a clear answer.

> Even if the user understands what he is doing: if `function-arity' can't
> answer the question whether a certain number of arguments is allowed, it
> is of not much value.

I think it can, and it will be.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 16:14                 ` Philipp Stephani
@ 2016-03-19 16:27                   ` Michael Heerdegen
  2016-03-19 16:27                   ` Eli Zaretskii
  1 sibling, 0 replies; 60+ messages in thread
From: Michael Heerdegen @ 2016-03-19 16:27 UTC (permalink / raw)
  To: Philipp Stephani; +Cc: Eli Zaretskii, emacs-devel

Philipp Stephani <p.stephani2@gmail.com> writes:

> Knowing the arity in cases like this requires either evaluating the
> function, or complex parsing of its definition. It is very reasonable
> to assume that (func-arity 'g) would return (0 . many), because that
> is g's arity as far as the Lisp interpreter is concerned.

Thanks, that's what I wanted to say, but couldn't formulate that
well. ;-)

Even if you would do a complex analysis of the definition: since we have
conditionals, and whether a function call is perform can depend on
conditions, and the result of the evaluated condition is only known when
running the code, even this complex analysis still would have
limitations.

Michael.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 16:14                 ` Philipp Stephani
  2016-03-19 16:27                   ` Michael Heerdegen
@ 2016-03-19 16:27                   ` Eli Zaretskii
  2016-03-19 16:30                     ` Philipp Stephani
  1 sibling, 1 reply; 60+ messages in thread
From: Eli Zaretskii @ 2016-03-19 16:27 UTC (permalink / raw)
  To: Philipp Stephani; +Cc: michael_heerdegen, emacs-devel

> From: Philipp Stephani <p.stephani2@gmail.com>
> Date: Sat, 19 Mar 2016 16:14:04 +0000
> Cc: emacs-devel@gnu.org
> 
> Knowing the arity in cases like this requires either evaluating the function, or complex parsing of its definition.

What do you mean by "complex parsing"?  Did you look at how the Lisp
interpreter does that?

> It is very reasonable to assume that (func-arity 'g) would return (0 . many), because that is g's arity as far as
> the Lisp interpreter is concerned. 

No, that's not g's arity:

  (g 10 202 30) => error -> "Wrong number of arguments"



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 16:27                   ` Eli Zaretskii
@ 2016-03-19 16:30                     ` Philipp Stephani
  2016-03-19 16:32                       ` Eli Zaretskii
  0 siblings, 1 reply; 60+ messages in thread
From: Philipp Stephani @ 2016-03-19 16:30 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: michael_heerdegen, emacs-devel

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

Eli Zaretskii <eliz@gnu.org> schrieb am Sa., 19. März 2016 um 17:28 Uhr:

> > From: Philipp Stephani <p.stephani2@gmail.com>
> > Date: Sat, 19 Mar 2016 16:14:04 +0000
> > Cc: emacs-devel@gnu.org
> >
> > Knowing the arity in cases like this requires either evaluating the
> function, or complex parsing of its definition.
>
> What do you mean by "complex parsing"?  Did you look at how the Lisp
> interpreter does that?
>

Yes, but parsing the arglist. But the arglist of g is (&rest args).


>
> > It is very reasonable to assume that (func-arity 'g) would return (0 .
> many), because that is g's arity as far as
> > the Lisp interpreter is concerned.
>
> No, that's not g's arity:
>
>   (g 10 202 30) => error -> "Wrong number of arguments"
>

This cannot be detected without evaluating the function.

[-- Attachment #2: Type: text/html, Size: 1451 bytes --]

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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 16:30                     ` Philipp Stephani
@ 2016-03-19 16:32                       ` Eli Zaretskii
  2016-03-19 16:34                         ` Philipp Stephani
  0 siblings, 1 reply; 60+ messages in thread
From: Eli Zaretskii @ 2016-03-19 16:32 UTC (permalink / raw)
  To: Philipp Stephani; +Cc: michael_heerdegen, emacs-devel

> From: Philipp Stephani <p.stephani2@gmail.com>
> Date: Sat, 19 Mar 2016 16:30:30 +0000
> Cc: michael_heerdegen@web.de, emacs-devel@gnu.org
> 
>  No, that's not g's arity:
> 
>  (g 10 202 30) => error -> "Wrong number of arguments"
> 
> This cannot be detected without evaluating the function. 

Are we discussing the usefulness of the function, or are we discussing
how best to implement it?



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 16:32                       ` Eli Zaretskii
@ 2016-03-19 16:34                         ` Philipp Stephani
  2016-03-19 16:46                           ` Philipp Stephani
  2016-03-19 16:47                           ` Eli Zaretskii
  0 siblings, 2 replies; 60+ messages in thread
From: Philipp Stephani @ 2016-03-19 16:34 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: michael_heerdegen, emacs-devel

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

Eli Zaretskii <eliz@gnu.org> schrieb am Sa., 19. März 2016 um 17:33 Uhr:

> > From: Philipp Stephani <p.stephani2@gmail.com>
> > Date: Sat, 19 Mar 2016 16:30:30 +0000
> > Cc: michael_heerdegen@web.de, emacs-devel@gnu.org
> >
> >  No, that's not g's arity:
> >
> >  (g 10 202 30) => error -> "Wrong number of arguments"
> >
> > This cannot be detected without evaluating the function.
>
> Are we discussing the usefulness of the function, or are we discussing
> how best to implement it?
>

We are discussing for which cases it can be implemented. It can be
implemented and would be useful for a wide range of functions, such as
those defined with defun. It cannot be implemented for functions like g;
that's just a limitation we have to live with.

[-- Attachment #2: Type: text/html, Size: 1290 bytes --]

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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 16:34                         ` Philipp Stephani
@ 2016-03-19 16:46                           ` Philipp Stephani
  2016-03-19 16:47                           ` Eli Zaretskii
  1 sibling, 0 replies; 60+ messages in thread
From: Philipp Stephani @ 2016-03-19 16:46 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: michael_heerdegen, emacs-devel

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

Philipp Stephani <p.stephani2@gmail.com> schrieb am Sa., 19. März 2016 um
17:34 Uhr:

> Eli Zaretskii <eliz@gnu.org> schrieb am Sa., 19. März 2016 um 17:33 Uhr:
>
>> > From: Philipp Stephani <p.stephani2@gmail.com>
>> > Date: Sat, 19 Mar 2016 16:30:30 +0000
>> > Cc: michael_heerdegen@web.de, emacs-devel@gnu.org
>> >
>> >  No, that's not g's arity:
>> >
>> >  (g 10 202 30) => error -> "Wrong number of arguments"
>> >
>> > This cannot be detected without evaluating the function.
>>
>> Are we discussing the usefulness of the function, or are we discussing
>> how best to implement it?
>>
>
> We are discussing for which cases it can be implemented. It can be
> implemented and would be useful for a wide range of functions, such as
> those defined with defun.
>

Example implementation (doesn't work with macros):

(defun function-arity (function)
  (setq function (indirect-function function))
  (cl-check-type function function)
  (if (subrp function)
      (subr-arity function)
    (let ((min 0) (max 0) optional)
      (dolist (arg (help-function-arglist function) (cons min max))
        (cond
         ((eq max 'many))
         ((eq arg '&optional) (setq optional t))
         ((eq arg '&rest) (setq max 'many))
         (t
          (unless optional (cl-incf min))
          (cl-incf max)))))))

[-- Attachment #2: Type: text/html, Size: 2381 bytes --]

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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 16:34                         ` Philipp Stephani
  2016-03-19 16:46                           ` Philipp Stephani
@ 2016-03-19 16:47                           ` Eli Zaretskii
  2016-03-19 17:16                             ` Philipp Stephani
  1 sibling, 1 reply; 60+ messages in thread
From: Eli Zaretskii @ 2016-03-19 16:47 UTC (permalink / raw)
  To: Philipp Stephani; +Cc: michael_heerdegen, emacs-devel

> From: Philipp Stephani <p.stephani2@gmail.com>
> Date: Sat, 19 Mar 2016 16:34:55 +0000
> Cc: michael_heerdegen@web.de, emacs-devel@gnu.org
> 
>  > (g 10 202 30) => error -> "Wrong number of arguments"
>  >
>  > This cannot be detected without evaluating the function.
> 
>  Are we discussing the usefulness of the function, or are we discussing
>  how best to implement it?
> 
> 
> We are discussing for which cases it can be implemented. It can be implemented and would be useful for a
> wide range of functions, such as those defined with defun. It cannot be implemented for functions like g; that's
> just a limitation we have to live with. 

That's one possibility.  Another is that the implementation will be
able to find out the truth, just like the Lisp interpreter does.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 14:26       ` Philipp Stephani
@ 2016-03-19 16:51         ` Paul Pogonyshev
  2016-03-19 18:09           ` Eli Zaretskii
       [not found]           ` <<83y49e731p.fsf@gnu.org>
  0 siblings, 2 replies; 60+ messages in thread
From: Paul Pogonyshev @ 2016-03-19 16:51 UTC (permalink / raw)
  To: Philipp Stephani; +Cc: emacs-devel


[-- Attachment #1.1: Type: text/plain, Size: 598 bytes --]

Approximate patch, without documentation.  If this is accepted, I can write
documentation too.  I submitted legal papers for FSF years ago.

    (func-arity 'car)                      (1 . 1)
    (func-arity 'caar)                     (1 . 1)
    (func-arity 'magit-log-all)            (0 . 2)
    (func-arity 'format)                   (1 . many)
    (func-arity (lambda (&rest x)))        (0 . many)

Return value is the same as with `subr-arity' except here any callable is
accepted and you don't need `symbol-function' or `indirect-function'.
Autoloading is also supported transparently.

Paul

[-- Attachment #1.2: Type: text/html, Size: 1044 bytes --]

[-- Attachment #2: func-arity.diff --]
[-- Type: text/plain, Size: 5316 bytes --]

diff --git a/src/bytecode.c b/src/bytecode.c
index 9ae2e82..ca04c28 100644
--- a/src/bytecode.c
+++ b/src/bytecode.c
@@ -1987,6 +1987,22 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
   return result;
 }
 
+Lisp_Object
+get_byte_code_arity (Lisp_Object args_template)
+{
+  if (INTEGERP (args_template))
+    {
+      ptrdiff_t at = XINT (args_template);
+      bool rest = (at & 128) != 0;
+      int mandatory = at & 127;
+      ptrdiff_t nonrest = at >> 8;
+
+      return Fcons (make_number (mandatory), rest ? Qmany : make_number (nonrest));
+    }
+  else
+    error ("Unknown args template!");
+}
+
 void
 syms_of_bytecode (void)
 {
diff --git a/src/eval.c b/src/eval.c
index 74b30e6..40ed24c 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -90,6 +90,7 @@ union specbinding *backtrace_top (void) EXTERNALLY_VISIBLE;
 
 static Lisp_Object funcall_lambda (Lisp_Object, ptrdiff_t, Lisp_Object *);
 static Lisp_Object apply_lambda (Lisp_Object, Lisp_Object, ptrdiff_t);
+static Lisp_Object lambda_arity (Lisp_Object);
 
 static Lisp_Object
 specpdl_symbol (union specbinding *pdl)
@@ -2934,6 +2935,122 @@ funcall_lambda (Lisp_Object fun, ptrdiff_t nargs,
   return unbind_to (count, val);
 }
 
+DEFUN ("func-arity", Ffunc_arity, Sfunc_arity, 1, 1, 0,
+       doc: /* Return minimum and maximum number of args allowed for FUNCTION.
+FUNCTION must be a function of some kind.
+The returned value is a pair (MIN . MAX).  MIN is the minimum number
+of args.  MAX is the maximum number or the symbol `many', for a
+function with `&rest' args, or `unevalled' for a special form.  */)
+  (Lisp_Object function)
+{
+  Lisp_Object original;
+  Lisp_Object funcar;
+  Lisp_Object result;
+  short minargs, maxargs;
+
+  original = function;
+
+ retry:
+
+  /* Optimize for no indirection.  */
+  function = original;
+  if (SYMBOLP (function) && !NILP (function)
+      && (function = XSYMBOL (function)->function, SYMBOLP (function)))
+    function = indirect_function (function);
+
+  if (SUBRP (function))
+    {
+      minargs = XSUBR (function)->min_args;
+      maxargs = XSUBR (function)->max_args;
+      result  = Fcons (make_number (minargs),
+                       maxargs == MANY ?        Qmany
+                       : maxargs == UNEVALLED ? Qunevalled
+                       :                        make_number (maxargs));
+    }
+  else if (COMPILEDP (function))
+    result = lambda_arity (function);
+  else
+    {
+      if (NILP (function))
+	xsignal1 (Qvoid_function, original);
+      if (!CONSP (function))
+	xsignal1 (Qinvalid_function, original);
+      funcar = XCAR (function);
+      if (!SYMBOLP (funcar))
+	xsignal1 (Qinvalid_function, original);
+      if (EQ (funcar, Qlambda)
+	  || EQ (funcar, Qclosure))
+	result = lambda_arity (function);
+      else if (EQ (funcar, Qautoload))
+	{
+	  Fautoload_do_load (function, original, Qnil);
+	  goto retry;
+	}
+      else
+	xsignal1 (Qinvalid_function, original);
+    }
+  return result;
+}
+
+/* FUN must be either a lambda-expression or a compiled-code object.  */
+static Lisp_Object
+lambda_arity (Lisp_Object fun)
+{
+  Lisp_Object val, syms_left, next;
+  ptrdiff_t minargs, maxargs;
+  bool optional;
+
+  if (CONSP (fun))
+    {
+      if (EQ (XCAR (fun), Qclosure))
+	{
+	  fun = XCDR (fun);	/* Drop `closure'.  */
+	  CHECK_LIST_CONS (fun, fun);
+	}
+      syms_left = XCDR (fun);
+      if (CONSP (syms_left))
+	syms_left = XCAR (syms_left);
+      else
+	xsignal1 (Qinvalid_function, fun);
+    }
+  else if (COMPILEDP (fun))
+    {
+      ptrdiff_t size = ASIZE (fun) & PSEUDOVECTOR_SIZE_MASK;
+      if (size <= COMPILED_STACK_DEPTH)
+	xsignal1 (Qinvalid_function, fun);
+      syms_left = AREF (fun, COMPILED_ARGLIST);
+      if (INTEGERP (syms_left))
+        return get_byte_code_arity (syms_left);
+    }
+  else
+    emacs_abort ();
+
+  minargs = maxargs = optional = 0;
+  for (; CONSP (syms_left); syms_left = XCDR (syms_left))
+    {
+      next = XCAR (syms_left);
+      if (!SYMBOLP (next))
+	xsignal1 (Qinvalid_function, fun);
+
+      if (EQ (next, Qand_rest))
+	return Fcons (make_number (minargs), Qmany);
+      else if (EQ (next, Qand_optional))
+	optional = 1;
+      else
+	{
+          if (!optional)
+            minargs++;
+          maxargs++;
+        }
+    }
+
+  if (!NILP (syms_left))
+    xsignal1 (Qinvalid_function, fun);
+
+  return Fcons (make_number (minargs), make_number (maxargs));
+}
+
+
 DEFUN ("fetch-bytecode", Ffetch_bytecode, Sfetch_bytecode,
        1, 1, 0,
        doc: /* If byte-compiled OBJECT is lazy-loaded, fetch it now.  */)
@@ -3808,6 +3925,7 @@ alist of active lexical bindings.  */);
   defsubr (&Seval);
   defsubr (&Sapply);
   defsubr (&Sfuncall);
+  defsubr (&Sfunc_arity);
   defsubr (&Srun_hooks);
   defsubr (&Srun_hook_with_args);
   defsubr (&Srun_hook_with_args_until_success);
diff --git a/src/lisp.h b/src/lisp.h
index d0abb24..cd0c0fc 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4214,6 +4214,7 @@ extern struct byte_stack *byte_stack_list;
 extern void relocate_byte_stack (void);
 extern Lisp_Object exec_byte_code (Lisp_Object, Lisp_Object, Lisp_Object,
 				   Lisp_Object, ptrdiff_t, Lisp_Object *);
+extern Lisp_Object get_byte_code_arity (Lisp_Object);
 
 /* Defined in macros.c.  */
 extern void init_macros (void);

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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 16:47                           ` Eli Zaretskii
@ 2016-03-19 17:16                             ` Philipp Stephani
  2016-03-19 17:48                               ` Eli Zaretskii
  0 siblings, 1 reply; 60+ messages in thread
From: Philipp Stephani @ 2016-03-19 17:16 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: michael_heerdegen, emacs-devel

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

Eli Zaretskii <eliz@gnu.org> schrieb am Sa., 19. März 2016 um 17:47 Uhr:

> > From: Philipp Stephani <p.stephani2@gmail.com>
> > Date: Sat, 19 Mar 2016 16:34:55 +0000
> > Cc: michael_heerdegen@web.de, emacs-devel@gnu.org
> >
> >  > (g 10 202 30) => error -> "Wrong number of arguments"
> >  >
> >  > This cannot be detected without evaluating the function.
> >
> >  Are we discussing the usefulness of the function, or are we discussing
> >  how best to implement it?
> >
> >
> > We are discussing for which cases it can be implemented. It can be
> implemented and would be useful for a
> > wide range of functions, such as those defined with defun. It cannot be
> implemented for functions like g; that's
> > just a limitation we have to live with.
>
> That's one possibility.  Another is that the implementation will be
> able to find out the truth, just like the Lisp interpreter does.
>

Presumably that would imply solving the halting problem.

[-- Attachment #2: Type: text/html, Size: 1532 bytes --]

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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 16:24                   ` Eli Zaretskii
@ 2016-03-19 17:43                     ` Michael Heerdegen
  2016-03-19 17:50                       ` Eli Zaretskii
  0 siblings, 1 reply; 60+ messages in thread
From: Michael Heerdegen @ 2016-03-19 17:43 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> I disagree with this conclusion.  The fact is that the Emacs Lisp
> interpreter does know how to produce a clear answer.

Imagine `f' accepts, say, three arguments.  Now, let's

(advice-add 'f :around
            (defun my-f-around-advice (orig-f &rest args)
              (let ((some-value (g args)))
                (if (function-p (car-safe some-value))
                    (apply (car some-value) (cdr some-value))
                  (if (h args)
                      (k args)
                    (apply orig-f some-value))))))

What should (function-arity #'f) return?


Michael.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 17:16                             ` Philipp Stephani
@ 2016-03-19 17:48                               ` Eli Zaretskii
  2016-03-19 17:49                                 ` Philipp Stephani
  0 siblings, 1 reply; 60+ messages in thread
From: Eli Zaretskii @ 2016-03-19 17:48 UTC (permalink / raw)
  To: Philipp Stephani; +Cc: michael_heerdegen, emacs-devel

> From: Philipp Stephani <p.stephani2@gmail.com>
> Date: Sat, 19 Mar 2016 17:16:55 +0000
> Cc: michael_heerdegen@web.de, emacs-devel@gnu.org
> 
>  > We are discussing for which cases it can be implemented. It can be implemented and would be
>  useful for a
>  > wide range of functions, such as those defined with defun. It cannot be implemented for functions like
>  g; that's
>  > just a limitation we have to live with.
> 
>  That's one possibility. Another is that the implementation will be
>  able to find out the truth, just like the Lisp interpreter does.
> 
> Presumably that would imply solving the halting problem. 

I guess we'll have to disagree about that.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 17:48                               ` Eli Zaretskii
@ 2016-03-19 17:49                                 ` Philipp Stephani
  2016-03-19 18:11                                   ` Eli Zaretskii
  0 siblings, 1 reply; 60+ messages in thread
From: Philipp Stephani @ 2016-03-19 17:49 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: michael_heerdegen, emacs-devel

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

Eli Zaretskii <eliz@gnu.org> schrieb am Sa., 19. März 2016 um 18:48 Uhr:

> > From: Philipp Stephani <p.stephani2@gmail.com>
> > Date: Sat, 19 Mar 2016 17:16:55 +0000
> > Cc: michael_heerdegen@web.de, emacs-devel@gnu.org
> >
> >  > We are discussing for which cases it can be implemented. It can be
> implemented and would be
> >  useful for a
> >  > wide range of functions, such as those defined with defun. It cannot
> be implemented for functions like
> >  g; that's
> >  > just a limitation we have to live with.
> >
> >  That's one possibility. Another is that the implementation will be
> >  able to find out the truth, just like the Lisp interpreter does.
> >
> > Presumably that would imply solving the halting problem.
>
> I guess we'll have to disagree about that.
>

You could convince me by posting a generic implementation that would figure
out the arity of g without executing it.

[-- Attachment #2: Type: text/html, Size: 1459 bytes --]

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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 17:43                     ` Michael Heerdegen
@ 2016-03-19 17:50                       ` Eli Zaretskii
  2016-03-19 17:59                         ` Michael Heerdegen
  0 siblings, 1 reply; 60+ messages in thread
From: Eli Zaretskii @ 2016-03-19 17:50 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: emacs-devel

> From: Michael Heerdegen <michael_heerdegen@web.de>
> Cc: emacs-devel@gnu.org
> Date: Sat, 19 Mar 2016 18:43:09 +0100
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > I disagree with this conclusion.  The fact is that the Emacs Lisp
> > interpreter does know how to produce a clear answer.
> 
> Imagine `f' accepts, say, three arguments.  Now, let's
> 
> (advice-add 'f :around
>             (defun my-f-around-advice (orig-f &rest args)
>               (let ((some-value (g args)))
>                 (if (function-p (car-safe some-value))
>                     (apply (car some-value) (cdr some-value))
>                   (if (h args)
>                       (k args)
>                     (apply orig-f some-value))))))
> 
> What should (function-arity #'f) return?

How is that relevant to the cited text?

And what is the purpose of continuing this argument?



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 17:50                       ` Eli Zaretskii
@ 2016-03-19 17:59                         ` Michael Heerdegen
  2016-03-19 18:14                           ` Eli Zaretskii
  0 siblings, 1 reply; 60+ messages in thread
From: Michael Heerdegen @ 2016-03-19 17:59 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> > > I disagree with this conclusion.  The fact is that the Emacs Lisp
> > > interpreter does know how to produce a clear answer.
> > 
> > Imagine `f' accepts, say, three arguments.  Now, let's
> > 
> > (advice-add 'f :around
> >             (defun my-f-around-advice (orig-f &rest args)
> >               (let ((some-value (g args)))
> >                 (if (function-p (car-safe some-value))
> >                     (apply (car some-value) (cdr some-value))
> >                   (if (h args)
> >                       (k args)
> >                     (apply orig-f some-value))))))
> > 
> > What should (function-arity #'f) return?
>
> How is that relevant to the cited text?

I had concluded that it is not possible to implement `function-arity' so
that it will always give a meaningful return value.  You disagreed
(that's the cited text), so I tried to give an example proving my
conclusion.  What did I miss?


Michael.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 16:51         ` Paul Pogonyshev
@ 2016-03-19 18:09           ` Eli Zaretskii
  2016-03-19 19:32             ` Michael Heerdegen
  2016-03-21 18:36             ` Paul Pogonyshev
       [not found]           ` <<83y49e731p.fsf@gnu.org>
  1 sibling, 2 replies; 60+ messages in thread
From: Eli Zaretskii @ 2016-03-19 18:09 UTC (permalink / raw)
  To: Paul Pogonyshev; +Cc: emacs-devel

> Date: Sat, 19 Mar 2016 17:51:39 +0100
> From: Paul Pogonyshev <pogonyshev@gmail.com>
> Cc: emacs-devel@gnu.org
> 
> Approximate patch, without documentation. If this is accepted, I can write documentation too. I submitted
> legal papers for FSF years ago.

Thanks.  A few comments below.  Let's wait for a few days to give
others a chance to comment.

> (func-arity 'car) (1 . 1)
> (func-arity 'caar) (1 . 1)
> (func-arity 'magit-log-all) (0 . 2)
> (func-arity 'format) (1 . many)
> (func-arity (lambda (&rest x))) (0 . many)

How about adding a few tests for this in test/ ?

> +Lisp_Object
> +get_byte_code_arity (Lisp_Object args_template)
> +{
> +  if (INTEGERP (args_template))
> +    {
> +      ptrdiff_t at = XINT (args_template);
> +      bool rest = (at & 128) != 0;
> +      int mandatory = at & 127;
> +      ptrdiff_t nonrest = at >> 8;
> +
> +      return Fcons (make_number (mandatory), rest ? Qmany : make_number (nonrest));
> +    }
> +  else
> +    error ("Unknown args template!");

It could also be a list (that was the old style).  Maybe we should
also support that, in case some old byte-compiled file is used?  I
think you already have the necessary code in lambda_arity.

> +  if (SUBRP (function))
> +    {
> +      minargs = XSUBR (function)->min_args;
> +      maxargs = XSUBR (function)->max_args;
> +      result  = Fcons (make_number (minargs),
> +                       maxargs == MANY ?        Qmany
> +                       : maxargs == UNEVALLED ? Qunevalled
> +                       :                        make_number (maxargs));
> +    }

We have sub-arity, so I think we should remove it and leave an alias
that will call this new function for backward compatibility.  Having
both sounds redundant.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 17:49                                 ` Philipp Stephani
@ 2016-03-19 18:11                                   ` Eli Zaretskii
  2016-03-19 18:35                                     ` Michael Heerdegen
  0 siblings, 1 reply; 60+ messages in thread
From: Eli Zaretskii @ 2016-03-19 18:11 UTC (permalink / raw)
  To: Philipp Stephani; +Cc: michael_heerdegen, emacs-devel

> From: Philipp Stephani <p.stephani2@gmail.com>
> Date: Sat, 19 Mar 2016 17:49:46 +0000
> Cc: michael_heerdegen@web.de, emacs-devel@gnu.org
> 
>  I guess we'll have to disagree about that.
> 
> You could convince me by posting a generic implementation that would figure out the arity of g without
> executing it. 

You could convince me by posting a formal proof that such an
implementation is impossible.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 17:59                         ` Michael Heerdegen
@ 2016-03-19 18:14                           ` Eli Zaretskii
  0 siblings, 0 replies; 60+ messages in thread
From: Eli Zaretskii @ 2016-03-19 18:14 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: emacs-devel

> From: Michael Heerdegen <michael_heerdegen@web.de>
> Cc: emacs-devel@gnu.org
> Date: Sat, 19 Mar 2016 18:59:14 +0100
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > > > I disagree with this conclusion.  The fact is that the Emacs Lisp
> > > > interpreter does know how to produce a clear answer.
> > > 
> > > Imagine `f' accepts, say, three arguments.  Now, let's
> > > 
> > > (advice-add 'f :around
> > >             (defun my-f-around-advice (orig-f &rest args)
> > >               (let ((some-value (g args)))
> > >                 (if (function-p (car-safe some-value))
> > >                     (apply (car some-value) (cdr some-value))
> > >                   (if (h args)
> > >                       (k args)
> > >                     (apply orig-f some-value))))))
> > > 
> > > What should (function-arity #'f) return?
> >
> > How is that relevant to the cited text?
> 
> I had concluded that it is not possible to implement `function-arity' so
> that it will always give a meaningful return value.  You disagreed
> (that's the cited text), so I tried to give an example proving my
> conclusion.  What did I miss?

The second sentence in the cited text.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 18:11                                   ` Eli Zaretskii
@ 2016-03-19 18:35                                     ` Michael Heerdegen
  0 siblings, 0 replies; 60+ messages in thread
From: Michael Heerdegen @ 2016-03-19 18:35 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Philipp Stephani, emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> You could convince me by posting a formal proof that such an
> implementation is impossible.

I can't prove the impossibility to implement semantics that are not
(yet) defined.

You haven't defined any semantics, you only said the interpreter would
know the answer.  But I don't know the question.

That's why I ask what for

(advice-add 'f :around
            (defun my-f-around-ad (orig-f &rest args)
              (let ((some-value (g args)))
                (if (function-p (car-safe some-value))
                    (apply (car some-value) (cdr some-value))
                  (if (h args)
                      (k args)
                    (apply orig-f some-value))))))

(function-arity 'f) should return.  Because I hardly see how any return
value would make sense, or more sense than some others.

Surely could you say it should return 17.  I cannot prove that it's not
possible to implement `function-arity' in a way so that it returns 17 in
this case, but my question would be how useful that return value would
be.


Michael.



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

* RE: Arbitrary function: find the number(s) of expected arguments
       [not found]           ` <<83y49e731p.fsf@gnu.org>
@ 2016-03-19 19:21             ` Drew Adams
  2016-04-18 18:43               ` Davis Herring
  0 siblings, 1 reply; 60+ messages in thread
From: Drew Adams @ 2016-03-19 19:21 UTC (permalink / raw)
  To: Eli Zaretskii, Paul Pogonyshev; +Cc: emacs-devel

> We have sub-arity, so I think we should remove it and leave an alias
> that will call this new function for backward compatibility.  Having
> both sounds redundant.

(I assume you meant `subr-arity', not `sub-arity'.)

Ignoring all the rest...

This sounds wrong to me.  Just calling the new code (which I
have not looked at, but which I presume does for arbitrary
functions what `subr-arity' does for primitives) would NOT
provide backward compatibility, precisely because it would
(presumably) NOT have the same behavior as `subr-arity' for
non-primitives - it would not raise an error.

Any existing code that depends on an error being raised by
`subr-arity' would break.  E.g.,

(condition-case err
    (subr-arity 'foo)
  (error (do-something)))



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 18:09           ` Eli Zaretskii
@ 2016-03-19 19:32             ` Michael Heerdegen
  2016-03-19 19:39               ` Eli Zaretskii
  2016-03-21 18:36             ` Paul Pogonyshev
  1 sibling, 1 reply; 60+ messages in thread
From: Michael Heerdegen @ 2016-03-19 19:32 UTC (permalink / raw)
  To: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> Thanks.  A few comments below.  Let's wait for a few days to give
> others a chance to comment.

That approach will reveal the (0 . many) problem as we discussed, e.g.


  (defun f (x y) x)

  (advice-add 'f :filter-return #'identity)

  (func-arity 'f)

  (0 . many)


So, to the question "can I call function X with that many args", the
answer can be "maybe".  In that case, the situation is not better than
now.  Since you know the answer not before run-time, this doesn't solve
the problem in the bug report, because you still have to write
alternative code for the "maybe" case - every time.

Don't take me wrong: this patch can be useful, e.g. for generating help
pages.  But IMO it isn't useful for coding.  On the contrary, I think
advertising `func-arity' to the user with this name will lead to a lot
of erroneous code.


Michael.




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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 19:32             ` Michael Heerdegen
@ 2016-03-19 19:39               ` Eli Zaretskii
  2016-03-19 20:59                 ` Michael Heerdegen
  0 siblings, 1 reply; 60+ messages in thread
From: Eli Zaretskii @ 2016-03-19 19:39 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: emacs-devel

> From: Michael Heerdegen <michael_heerdegen@web.de>
> Date: Sat, 19 Mar 2016 20:32:25 +0100
> 
> So, to the question "can I call function X with that many args", the
> answer can be "maybe".  In that case, the situation is not better than
> now.  Since you know the answer not before run-time, this doesn't solve
> the problem in the bug report, because you still have to write
> alternative code for the "maybe" case - every time.
> 
> Don't take me wrong: this patch can be useful, e.g. for generating help
> pages.  But IMO it isn't useful for coding.  On the contrary, I think
> advertising `func-arity' to the user with this name will lead to a lot
> of erroneous code.

I guess you won't be using this, then.  And neither will I, frankly,
as I never had the need for such a function.  But if someone does, I
see no reason not to provide it.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 13:10       ` Eli Zaretskii
  2016-03-19 13:42         ` Paul Pogonyshev
  2016-03-19 13:54         ` Michael Heerdegen
@ 2016-03-19 19:52         ` Stefan Monnier
  2016-03-19 20:33           ` Eli Zaretskii
  2016-03-26 15:55           ` Elias Mårtenson
  2 siblings, 2 replies; 60+ messages in thread
From: Stefan Monnier @ 2016-03-19 19:52 UTC (permalink / raw)
  To: emacs-devel

> Patches to add func-arity to Emacs are welcome.

Actually, I don't welcome them.  I always resisted them, because I think
that using func-arity is a bad idea.

As mentioned to my reply in the corresponding thread, you're better off
just making the call and catching the potential
`wrong-number-of-arguments' signal.


        Stefan


PS: Little quiz: what should be the func-arity
of (lambda (&rest x) (apply #'car x))?




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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 19:52         ` Stefan Monnier
@ 2016-03-19 20:33           ` Eli Zaretskii
  2016-03-19 22:43             ` Stefan Monnier
  2016-03-26 15:55           ` Elias Mårtenson
  1 sibling, 1 reply; 60+ messages in thread
From: Eli Zaretskii @ 2016-03-19 20:33 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

> From: Stefan Monnier <monnier@iro.umontreal.ca>
> Date: Sat, 19 Mar 2016 15:52:45 -0400
> 
> > Patches to add func-arity to Emacs are welcome.
> 
> Actually, I don't welcome them.  I always resisted them, because I think
> that using func-arity is a bad idea.

I don't see why would that be such a bad idea that it justifies
rejecting a patch.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 19:39               ` Eli Zaretskii
@ 2016-03-19 20:59                 ` Michael Heerdegen
  0 siblings, 0 replies; 60+ messages in thread
From: Michael Heerdegen @ 2016-03-19 20:59 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

> I guess you won't be using this, then.  And neither will I, frankly,
> as I never had the need for such a function.  But if someone does, I
> see no reason not to provide it.

Frankly, I'm not against adding it, too, in that form.  But the
documentation should clearly state the limitations (that the return
value of advised functions is not meaningful etc.).

(Personally, I would prefer (min . any) instead of (min . many).  A
function accepts any number of args, min at minimum.  That don't need to
be many).

And I would sleep much better tonight if it would get a name like
"help-function-arity".  I would even try to find a reason to make use of
it in help.el ;-)


Regards,

Michael.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 20:33           ` Eli Zaretskii
@ 2016-03-19 22:43             ` Stefan Monnier
  0 siblings, 0 replies; 60+ messages in thread
From: Stefan Monnier @ 2016-03-19 22:43 UTC (permalink / raw)
  To: emacs-devel

> I don't see why would that be such a bad idea that it justifies
> rejecting a patch.

I don't think we should accept addition of functions which are never
needed because every code that could use them would be better served by
something else.

I'm fully aware that it's a losing battle, tho.


        Stefan




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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 18:09           ` Eli Zaretskii
  2016-03-19 19:32             ` Michael Heerdegen
@ 2016-03-21 18:36             ` Paul Pogonyshev
  2016-03-25  8:44               ` Eli Zaretskii
  1 sibling, 1 reply; 60+ messages in thread
From: Paul Pogonyshev @ 2016-03-21 18:36 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel


[-- Attachment #1.1: Type: text/plain, Size: 1034 bytes --]

Eli Zaretskii wrote:
> How about adding a few tests for this in test/ ?

Done in the attached second patch version.

> > +Lisp_Object
> > +get_byte_code_arity (Lisp_Object args_template)
> > +{
> > +  if (INTEGERP (args_template))
>
> It could also be a list (that was the old style). [...]

Apparently no.  I just based get_byte_code_arity() on what
exec_byte_code() does.

> We have sub-arity, so I think we should remove it and leave an alias
> that will call this new function for backward compatibility.  Having
> both sounds redundant.

Drew Adams wrote:
> This sounds wrong to me.  Just calling the new code (which I
> have not looked at, but which I presume does for arbitrary
> functions what `subr-arity' does for primitives) would NOT
> provide backward compatibility, precisely because it would
> (presumably) NOT have the same behavior as `subr-arity' for
> non-primitives - it would not raise an error.

The patch itself doesn't touch `subr-arity'. Whether to alias
it to `func-arity' or not can be decided later.

Paul

[-- Attachment #1.2: Type: text/html, Size: 3314 bytes --]

[-- Attachment #2: func-arity.diff --]
[-- Type: text/plain, Size: 6110 bytes --]

diff --git a/src/bytecode.c b/src/bytecode.c
index 9ae2e82..ca04c28 100644
--- a/src/bytecode.c
+++ b/src/bytecode.c
@@ -1987,6 +1987,22 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
   return result;
 }
 
+Lisp_Object
+get_byte_code_arity (Lisp_Object args_template)
+{
+  if (INTEGERP (args_template))
+    {
+      ptrdiff_t at = XINT (args_template);
+      bool rest = (at & 128) != 0;
+      int mandatory = at & 127;
+      ptrdiff_t nonrest = at >> 8;
+
+      return Fcons (make_number (mandatory), rest ? Qmany : make_number (nonrest));
+    }
+  else
+    error ("Unknown args template!");
+}
+
 void
 syms_of_bytecode (void)
 {
diff --git a/src/eval.c b/src/eval.c
index 74b30e6..40ed24c 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -90,6 +90,7 @@ union specbinding *backtrace_top (void) EXTERNALLY_VISIBLE;
 
 static Lisp_Object funcall_lambda (Lisp_Object, ptrdiff_t, Lisp_Object *);
 static Lisp_Object apply_lambda (Lisp_Object, Lisp_Object, ptrdiff_t);
+static Lisp_Object lambda_arity (Lisp_Object);
 
 static Lisp_Object
 specpdl_symbol (union specbinding *pdl)
@@ -2934,6 +2935,122 @@ funcall_lambda (Lisp_Object fun, ptrdiff_t nargs,
   return unbind_to (count, val);
 }
 
+DEFUN ("func-arity", Ffunc_arity, Sfunc_arity, 1, 1, 0,
+       doc: /* Return minimum and maximum number of args allowed for FUNCTION.
+FUNCTION must be a function of some kind.
+The returned value is a pair (MIN . MAX).  MIN is the minimum number
+of args.  MAX is the maximum number or the symbol `many', for a
+function with `&rest' args, or `unevalled' for a special form.  */)
+  (Lisp_Object function)
+{
+  Lisp_Object original;
+  Lisp_Object funcar;
+  Lisp_Object result;
+  short minargs, maxargs;
+
+  original = function;
+
+ retry:
+
+  /* Optimize for no indirection.  */
+  function = original;
+  if (SYMBOLP (function) && !NILP (function)
+      && (function = XSYMBOL (function)->function, SYMBOLP (function)))
+    function = indirect_function (function);
+
+  if (SUBRP (function))
+    {
+      minargs = XSUBR (function)->min_args;
+      maxargs = XSUBR (function)->max_args;
+      result  = Fcons (make_number (minargs),
+                       maxargs == MANY ?        Qmany
+                       : maxargs == UNEVALLED ? Qunevalled
+                       :                        make_number (maxargs));
+    }
+  else if (COMPILEDP (function))
+    result = lambda_arity (function);
+  else
+    {
+      if (NILP (function))
+	xsignal1 (Qvoid_function, original);
+      if (!CONSP (function))
+	xsignal1 (Qinvalid_function, original);
+      funcar = XCAR (function);
+      if (!SYMBOLP (funcar))
+	xsignal1 (Qinvalid_function, original);
+      if (EQ (funcar, Qlambda)
+	  || EQ (funcar, Qclosure))
+	result = lambda_arity (function);
+      else if (EQ (funcar, Qautoload))
+	{
+	  Fautoload_do_load (function, original, Qnil);
+	  goto retry;
+	}
+      else
+	xsignal1 (Qinvalid_function, original);
+    }
+  return result;
+}
+
+/* FUN must be either a lambda-expression or a compiled-code object.  */
+static Lisp_Object
+lambda_arity (Lisp_Object fun)
+{
+  Lisp_Object val, syms_left, next;
+  ptrdiff_t minargs, maxargs;
+  bool optional;
+
+  if (CONSP (fun))
+    {
+      if (EQ (XCAR (fun), Qclosure))
+	{
+	  fun = XCDR (fun);	/* Drop `closure'.  */
+	  CHECK_LIST_CONS (fun, fun);
+	}
+      syms_left = XCDR (fun);
+      if (CONSP (syms_left))
+	syms_left = XCAR (syms_left);
+      else
+	xsignal1 (Qinvalid_function, fun);
+    }
+  else if (COMPILEDP (fun))
+    {
+      ptrdiff_t size = ASIZE (fun) & PSEUDOVECTOR_SIZE_MASK;
+      if (size <= COMPILED_STACK_DEPTH)
+	xsignal1 (Qinvalid_function, fun);
+      syms_left = AREF (fun, COMPILED_ARGLIST);
+      if (INTEGERP (syms_left))
+        return get_byte_code_arity (syms_left);
+    }
+  else
+    emacs_abort ();
+
+  minargs = maxargs = optional = 0;
+  for (; CONSP (syms_left); syms_left = XCDR (syms_left))
+    {
+      next = XCAR (syms_left);
+      if (!SYMBOLP (next))
+	xsignal1 (Qinvalid_function, fun);
+
+      if (EQ (next, Qand_rest))
+	return Fcons (make_number (minargs), Qmany);
+      else if (EQ (next, Qand_optional))
+	optional = 1;
+      else
+	{
+          if (!optional)
+            minargs++;
+          maxargs++;
+        }
+    }
+
+  if (!NILP (syms_left))
+    xsignal1 (Qinvalid_function, fun);
+
+  return Fcons (make_number (minargs), make_number (maxargs));
+}
+
+
 DEFUN ("fetch-bytecode", Ffetch_bytecode, Sfetch_bytecode,
        1, 1, 0,
        doc: /* If byte-compiled OBJECT is lazy-loaded, fetch it now.  */)
@@ -3808,6 +3925,7 @@ alist of active lexical bindings.  */);
   defsubr (&Seval);
   defsubr (&Sapply);
   defsubr (&Sfuncall);
+  defsubr (&Sfunc_arity);
   defsubr (&Srun_hooks);
   defsubr (&Srun_hook_with_args);
   defsubr (&Srun_hook_with_args_until_success);
diff --git a/src/lisp.h b/src/lisp.h
index d0abb24..cd0c0fc 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4214,6 +4214,7 @@ extern struct byte_stack *byte_stack_list;
 extern void relocate_byte_stack (void);
 extern Lisp_Object exec_byte_code (Lisp_Object, Lisp_Object, Lisp_Object,
 				   Lisp_Object, ptrdiff_t, Lisp_Object *);
+extern Lisp_Object get_byte_code_arity (Lisp_Object);
 
 /* Defined in macros.c.  */
 extern void init_macros (void);
diff --git a/test/src/fns-tests.el b/test/src/fns-tests.el
index 8617369..89eed96 100644
--- a/test/src/fns-tests.el
+++ b/test/src/fns-tests.el
@@ -208,3 +208,12 @@
   (should (string-version-lessp "foo1.25.5.png" "foo1.125.5"))
   (should (string-version-lessp "2" "1245"))
   (should (not (string-version-lessp "1245" "2"))))
+
+(ert-deftest fns-tests-func-arity ()
+  (should (equal (func-arity 'car) '(1 . 1)))
+  (should (equal (func-arity 'caar) '(1 . 1)))
+  (should (equal (func-arity 'format) '(1 . many)))
+  (should (equal (func-arity 'Info-goto-node) '(1 . 3)))
+  (should (equal (func-arity (lambda (&rest x))) '(0 . many)))
+  (should (equal (func-arity (eval (lambda (x &optional y)) nil)) '(1 . 2)))
+  (should (equal (func-arity (eval (lambda (x &optional y)) t)) '(1 . 2))))

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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-21 18:36             ` Paul Pogonyshev
@ 2016-03-25  8:44               ` Eli Zaretskii
  2016-03-25 16:16                 ` Paul Pogonyshev
  0 siblings, 1 reply; 60+ messages in thread
From: Eli Zaretskii @ 2016-03-25  8:44 UTC (permalink / raw)
  To: Paul Pogonyshev; +Cc: emacs-devel

> Date: Mon, 21 Mar 2016 19:36:45 +0100
> From: Paul Pogonyshev <pogonyshev@gmail.com>
> Cc: emacs-devel@gnu.org
> 
> > We have sub-arity, so I think we should remove it and leave an alias
> > that will call this new function for backward compatibility. Having
> > both sounds redundant.
> 
> Drew Adams wrote:
> 
> > This sounds wrong to me. Just calling the new code (which I
> > have not looked at, but which I presume does for arbitrary
> > functions what `subr-arity' does for primitives) would NOT
> > provide backward compatibility, precisely because it would
> > (presumably) NOT have the same behavior as `subr-arity' for
> > non-primitives - it would not raise an error.
> 
> The patch itself doesn't touch `subr-arity'. Whether to alias
> it to `func-arity' or not can be decided later.

If we are going to keep sub-arity, I'd prefer if this new function
called it, instead of copying its code inline.

Also, I believe you said you'd write the documentation?  Could you
please add that?  Then the patch will be ready to go in, I think.

Thanks.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-25  8:44               ` Eli Zaretskii
@ 2016-03-25 16:16                 ` Paul Pogonyshev
  2016-03-25 16:35                   ` Drew Adams
  2016-03-26  8:27                   ` Eli Zaretskii
  0 siblings, 2 replies; 60+ messages in thread
From: Paul Pogonyshev @ 2016-03-25 16:16 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel


[-- Attachment #1.1: Type: text/plain, Size: 723 bytes --]

Eli Zaretskii wrote:

> If we are going to keep sub-arity, I'd prefer if this new function
> called it, instead of copying its code inline.
>

Done.


> Also, I believe you said you'd write the documentation?  Could you
> please add that?  Then the patch will be ready to go in, I think.
>

In the attached patch I modified `doc/lispref/functions.texi': text about
`subr-arity' is moved to a new section above about `func-arity' and
adapted as needed.  `subr-arity' is still in the documentation, but I
replaced its description with an advice to use `func-arity' instead. Is
that enough?

Do you still need changelog entries? Long time since I committed
anything to Emacs, maybe you finally got rid of them (I hope)?

Paul

[-- Attachment #1.2: Type: text/html, Size: 1661 bytes --]

[-- Attachment #2: func-arity.diff --]
[-- Type: text/plain, Size: 7490 bytes --]

diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi
index a2e94c3..559bf00 100644
--- a/doc/lispref/functions.texi
+++ b/doc/lispref/functions.texi
@@ -143,6 +143,18 @@ function, i.e., can be passed to @code{funcall}.  Note that
 and returns @code{nil} for special forms.
 @end defun
 
+  It is also possible to find out how many arguments an arbitrary
+function expects:
+
+@defun func-arity function
+This function provides information about the argument list of given
+@var{function}.  The returned value is a pair
+@code{(@var{min} . @var{max})}.  @var{min} is the minimum number of
+args.  @var{max} is the maximum number or the symbol @code{many}, for a
+function with @code{&rest} arguments, or the symbol @code{unevalled} if
+@var{function} is a special form.
+@end defun
+
 @noindent
 Unlike @code{functionp}, the next three functions do @emph{not} treat
 a symbol as its function definition.
@@ -176,12 +188,9 @@ function.  For example:
 @end defun
 
 @defun subr-arity subr
-This function provides information about the argument list of a
-primitive, @var{subr}.  The returned value is a pair
-@code{(@var{min} . @var{max})}.  @var{min} is the minimum number of
-args.  @var{max} is the maximum number or the symbol @code{many}, for a
-function with @code{&rest} arguments, or the symbol @code{unevalled} if
-@var{subr} is a special form.
+Works like @code{func-arity}, but only for built-in functions and
+without symbol indirection.  New code should use @code{func-arity}
+instead.
 @end defun
 
 @node Lambda Expressions
diff --git a/src/bytecode.c b/src/bytecode.c
index 9ae2e82..8108b17 100644
--- a/src/bytecode.c
+++ b/src/bytecode.c
@@ -1987,6 +1987,23 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
   return result;
 }
 
+/* `args_template' has the same meaning as in exec_byte_code() above. */
+Lisp_Object
+get_byte_code_arity (Lisp_Object args_template)
+{
+  if (INTEGERP (args_template))
+    {
+      ptrdiff_t at = XINT (args_template);
+      bool rest = (at & 128) != 0;
+      int mandatory = at & 127;
+      ptrdiff_t nonrest = at >> 8;
+
+      return Fcons (make_number (mandatory), rest ? Qmany : make_number (nonrest));
+    }
+  else
+    error ("Unknown args template!");
+}
+
 void
 syms_of_bytecode (void)
 {
diff --git a/src/eval.c b/src/eval.c
index 74b30e6..e0493c6 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -90,6 +90,7 @@ union specbinding *backtrace_top (void) EXTERNALLY_VISIBLE;
 
 static Lisp_Object funcall_lambda (Lisp_Object, ptrdiff_t, Lisp_Object *);
 static Lisp_Object apply_lambda (Lisp_Object, Lisp_Object, ptrdiff_t);
+static Lisp_Object lambda_arity (Lisp_Object);
 
 static Lisp_Object
 specpdl_symbol (union specbinding *pdl)
@@ -2934,6 +2935,115 @@ funcall_lambda (Lisp_Object fun, ptrdiff_t nargs,
   return unbind_to (count, val);
 }
 
+DEFUN ("func-arity", Ffunc_arity, Sfunc_arity, 1, 1, 0,
+       doc: /* Return minimum and maximum number of args allowed for FUNCTION.
+FUNCTION must be a function of some kind.
+The returned value is a pair (MIN . MAX).  MIN is the minimum number
+of args.  MAX is the maximum number or the symbol `many', for a
+function with `&rest' args, or `unevalled' for a special form.  */)
+  (Lisp_Object function)
+{
+  Lisp_Object original;
+  Lisp_Object funcar;
+  Lisp_Object result;
+  short minargs, maxargs;
+
+  original = function;
+
+ retry:
+
+  /* Optimize for no indirection.  */
+  function = original;
+  if (SYMBOLP (function) && !NILP (function)
+      && (function = XSYMBOL (function)->function, SYMBOLP (function)))
+    function = indirect_function (function);
+
+  if (SUBRP (function))
+    result = Fsubr_arity (function);
+  else if (COMPILEDP (function))
+    result = lambda_arity (function);
+  else
+    {
+      if (NILP (function))
+	xsignal1 (Qvoid_function, original);
+      if (!CONSP (function))
+	xsignal1 (Qinvalid_function, original);
+      funcar = XCAR (function);
+      if (!SYMBOLP (funcar))
+	xsignal1 (Qinvalid_function, original);
+      if (EQ (funcar, Qlambda)
+	  || EQ (funcar, Qclosure))
+	result = lambda_arity (function);
+      else if (EQ (funcar, Qautoload))
+	{
+	  Fautoload_do_load (function, original, Qnil);
+	  goto retry;
+	}
+      else
+	xsignal1 (Qinvalid_function, original);
+    }
+  return result;
+}
+
+/* FUN must be either a lambda-expression or a compiled-code object.  */
+static Lisp_Object
+lambda_arity (Lisp_Object fun)
+{
+  Lisp_Object val, syms_left, next;
+  ptrdiff_t minargs, maxargs;
+  bool optional;
+
+  if (CONSP (fun))
+    {
+      if (EQ (XCAR (fun), Qclosure))
+	{
+	  fun = XCDR (fun);	/* Drop `closure'.  */
+	  CHECK_LIST_CONS (fun, fun);
+	}
+      syms_left = XCDR (fun);
+      if (CONSP (syms_left))
+	syms_left = XCAR (syms_left);
+      else
+	xsignal1 (Qinvalid_function, fun);
+    }
+  else if (COMPILEDP (fun))
+    {
+      ptrdiff_t size = ASIZE (fun) & PSEUDOVECTOR_SIZE_MASK;
+      if (size <= COMPILED_STACK_DEPTH)
+	xsignal1 (Qinvalid_function, fun);
+      syms_left = AREF (fun, COMPILED_ARGLIST);
+      if (INTEGERP (syms_left))
+        return get_byte_code_arity (syms_left);
+    }
+  else
+    emacs_abort ();
+
+  minargs = maxargs = optional = 0;
+  for (; CONSP (syms_left); syms_left = XCDR (syms_left))
+    {
+      next = XCAR (syms_left);
+      if (!SYMBOLP (next))
+	xsignal1 (Qinvalid_function, fun);
+
+      if (EQ (next, Qand_rest))
+	return Fcons (make_number (minargs), Qmany);
+      else if (EQ (next, Qand_optional))
+	optional = 1;
+      else
+	{
+          if (!optional)
+            minargs++;
+          maxargs++;
+        }
+    }
+
+  if (!NILP (syms_left))
+    xsignal1 (Qinvalid_function, fun);
+
+  return Fcons (make_number (minargs), make_number (maxargs));
+}
+
+
 DEFUN ("fetch-bytecode", Ffetch_bytecode, Sfetch_bytecode,
        1, 1, 0,
        doc: /* If byte-compiled OBJECT is lazy-loaded, fetch it now.  */)
@@ -3808,6 +3918,7 @@ alist of active lexical bindings.  */);
   defsubr (&Seval);
   defsubr (&Sapply);
   defsubr (&Sfuncall);
+  defsubr (&Sfunc_arity);
   defsubr (&Srun_hooks);
   defsubr (&Srun_hook_with_args);
   defsubr (&Srun_hook_with_args_until_success);
diff --git a/src/lisp.h b/src/lisp.h
index e606ffa..7c8b452 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4215,6 +4215,7 @@ extern struct byte_stack *byte_stack_list;
 extern void relocate_byte_stack (void);
 extern Lisp_Object exec_byte_code (Lisp_Object, Lisp_Object, Lisp_Object,
 				   Lisp_Object, ptrdiff_t, Lisp_Object *);
+extern Lisp_Object get_byte_code_arity (Lisp_Object);
 
 /* Defined in macros.c.  */
 extern void init_macros (void);
diff --git a/test/src/fns-tests.el b/test/src/fns-tests.el
index 8617369..57ff0c5 100644
--- a/test/src/fns-tests.el
+++ b/test/src/fns-tests.el
@@ -208,3 +208,13 @@
   (should (string-version-lessp "foo1.25.5.png" "foo1.125.5"))
   (should (string-version-lessp "2" "1245"))
   (should (not (string-version-lessp "1245" "2"))))
+
+(ert-deftest fns-tests-func-arity ()
+  (should (equal (func-arity 'car) '(1 . 1)))
+  (should (equal (func-arity 'caar) '(1 . 1)))
+  (should (equal (func-arity 'format) '(1 . many)))
+  (should (equal (func-arity 'Info-goto-node) '(1 . 3)))
+  (should (equal (func-arity (lambda (&rest x))) '(0 . many)))
+  (should (equal (func-arity (eval (lambda (x &optional y)) nil)) '(1 . 2)))
+  (should (equal (func-arity (eval (lambda (x &optional y)) t)) '(1 . 2)))
+  (should (equal (func-arity 'let) '(1 . unevalled))))

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

* RE: Arbitrary function: find the number(s) of expected arguments
  2016-03-25 16:16                 ` Paul Pogonyshev
@ 2016-03-25 16:35                   ` Drew Adams
  2016-03-25 17:16                     ` Paul Pogonyshev
  2016-03-25 17:39                     ` Arbitrary function: find the number(s) of expected arguments Eli Zaretskii
  2016-03-26  8:27                   ` Eli Zaretskii
  1 sibling, 2 replies; 60+ messages in thread
From: Drew Adams @ 2016-03-25 16:35 UTC (permalink / raw)
  To: Paul Pogonyshev, Eli Zaretskii; +Cc: emacs-devel

>  `subr-arity' is still in the documentation, but I replaced its
> description with an advice to use `func-arity' instead.

Again, that is wrong, IMO.

IIUC, you _cannot_ use `func-arity' to test whether something
is a subr.  You can use `subr_arity' to give you the arity of
a subr AND to raise an error if the argument is not a subr.
The latter behavior is not available in `func-arity'.

The proper doc for `subr-arity' is to say what it _does_,
including that it raises an error for non-subr arg.  Its doc
can, and should, refer to `func-arity', but only as a way to
test the arity of _any_ function - not as a recommended
replacement for `subr-arity'.

IOW, I am repeating the same argument I made before, when
I said that `subr-arity' should not be deprecated and
simply replaced by `func-arity'.

If my argument is being rejected (in effect - in the new doc
string) then why are we not doing that openly (deprecating
`subr-arity' and replacing it with `func-arity')?

Either `func-arity' is a proper replacement for `subr-arity'
or it is not.  (I think it is not.)  If it is, then replace
it properly.

In addition, the new doc string for `subr-arity' also suffers
from making readers work extra hard.  They need to refer to
the doc of `func-arity', not only for the general description
of the behavior and each of the parameters, but also to
understand the difference wrt symbol indirection.

This is a step backward.  Unless we are really deprecating
and replacing it, we should document `subr-arity' properly,
as before, with the addition of cross-ref to see `func-arity',
stating that it handles any type of function.

IOW, if you want the arity of an arbitrary function, you can
use `func-arity' to get that.  That's all.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-25 16:35                   ` Drew Adams
@ 2016-03-25 17:16                     ` Paul Pogonyshev
  2016-03-25 18:19                       ` Drew Adams
  2016-03-25 17:39                     ` Arbitrary function: find the number(s) of expected arguments Eli Zaretskii
  1 sibling, 1 reply; 60+ messages in thread
From: Paul Pogonyshev @ 2016-03-25 17:16 UTC (permalink / raw)
  To: Drew Adams; +Cc: Eli Zaretskii, emacs-devel

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

Drew Adams wrote:
> IIUC, you _cannot_ use `func-arity' to test whether something
> is a subr.

Yes, but you can and should use `subrp' for that.

> IOW, I am repeating the same argument I made before, when
> I said that `subr-arity' should not be deprecated and
> simply replaced by `func-arity'.

I understood it as argument against aliasing `subr-arity' to
the new function: this can break _existing_ code if it relies
on the fact that `subr-arity' signals an error when called with
anything, but builtin.

> This is a step backward.  Unless we are really deprecating
> and replacing it, we should document `subr-arity' properly,
> as before, with the addition of cross-ref to see `func-arity',
> stating that it handles any type of function.

I personally don't see why we need two functions for this.
So, I would deprecate `subr-arity', but keep it around for
backward compatibility.

On the other hand, I don't really care. All I want is that there
is `func-arity' that works for _any_ function. I'm not attached
to anything in the patch and as long as `func-arity' works, feel
free to change anything.

Paul

[-- Attachment #2: Type: text/html, Size: 2243 bytes --]

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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-25 16:35                   ` Drew Adams
  2016-03-25 17:16                     ` Paul Pogonyshev
@ 2016-03-25 17:39                     ` Eli Zaretskii
  2016-03-25 18:31                       ` Drew Adams
  1 sibling, 1 reply; 60+ messages in thread
From: Eli Zaretskii @ 2016-03-25 17:39 UTC (permalink / raw)
  To: Drew Adams; +Cc: pogonyshev, emacs-devel

> Date: Fri, 25 Mar 2016 09:35:20 -0700 (PDT)
> From: Drew Adams <drew.adams@oracle.com>
> Cc: emacs-devel@gnu.org
> 
> >  `subr-arity' is still in the documentation, but I replaced its
> > description with an advice to use `func-arity' instead.
> 
> Again, that is wrong, IMO.

It isn't.

> IIUC, you _cannot_ use `func-arity' to test whether something
> is a subr.

You have subrp for that; using subr-arity for this purpose borders on
the ridiculous.  It's certainly unclean.

> IOW, I am repeating the same argument I made before, when
> I said that `subr-arity' should not be deprecated and
> simply replaced by `func-arity'.

You were wrong then, and you are wrong now.

> If my argument is being rejected (in effect - in the new doc
> string) then why are we not doing that openly (deprecating
> `subr-arity' and replacing it with `func-arity')?

Because you objected, and I'd rather not start yet another endless
discussion.



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

* RE: Arbitrary function: find the number(s) of expected arguments
  2016-03-25 17:16                     ` Paul Pogonyshev
@ 2016-03-25 18:19                       ` Drew Adams
  2016-03-25 18:28                         ` Clément Pit--Claudel
  0 siblings, 1 reply; 60+ messages in thread
From: Drew Adams @ 2016-03-25 18:19 UTC (permalink / raw)
  To: Paul Pogonyshev; +Cc: Eli Zaretskii, emacs-devel

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

(Please use plain-text for this mailing list from now on. I won't bother trying to convert the formatting this time.)

 

> IIUC, you _cannot_ use `func-arity' to test whether something

> is a subr.

Yes, but you can and should use `subrp' for that.

Of course you can. But that's irrelevant here. This is about what `subr-arity' does. Its behavior is not the same as `func-arity' for a non-subr. If we are not deprecating `subr-arity' then it is not enough to send users to the doc for `func-arity'.

> IOW, I am repeating the same argument I made before, when

> I said that `subr-arity' should not be deprecated and

> simply replaced by `func-arity'.

I understood it as argument against aliasing `subr-arity' to

the new function: this can break _existing_ code if it relies

on the fact that `subr-arity' signals an error when called with

anything, but builtin.

Yes. And?

The same argument applies to just having its doc string tell users to use `func-arity'. That might not break existing code, but it breaks the doc string. It no longer says what function `subr-arity' does. And it gives the false impression that `func-arity' does the same thing. In fact, `func-arity' does something different if the arg is not a subr.

> This is a step backward.  Unless we are really deprecating

> and replacing it, we should document `subr-arity' properly,

> as before, with the addition of cross-ref to see `func-arity',

> stating that it handles any type of function.

I personally don't see why we need two functions for this.

So, I would deprecate `subr-arity', but keep it around for

backward compatibility.

I personally feel the opposite - see my argument about not breaking existing code. But if that will be the decision then that's different. So far, there has been no decision to deprecate `subr-arity', AFAIK.

On the other hand, I don't really care. All I want is that there

is `func-arity' that works for _any_ function. I'm not attached

to anything in the patch and as long as `func-arity' works,

`func-arity' works for any function. So your want is satisfied.

However, obtaining your want at the expense of also breaking a doc string is not right.

feel free to change anything.

I'm not going to change the broken doc string. But I certainly hope that someone will.

When code is improved, if that change entails needing to change the doc, then the doc should be updated appropriately.

[-- Attachment #2: Type: text/html, Size: 7369 bytes --]

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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-25 18:19                       ` Drew Adams
@ 2016-03-25 18:28                         ` Clément Pit--Claudel
  2016-03-25 18:51                           ` Use plain-text for mail [was: Arbitrary function: find the number(s) of expected arguments] Drew Adams
  0 siblings, 1 reply; 60+ messages in thread
From: Clément Pit--Claudel @ 2016-03-25 18:28 UTC (permalink / raw)
  To: emacs-devel


[-- Attachment #1.1: Type: text/plain, Size: 2859 bytes --]

Drew, your message is actually much harder to read in Thunderbird that the one you replied to. Maybe using a plain text response in such cases would be best? I had no trouble reading Paul's email, but yours was tricky.

On 03/25/2016 02:19 PM, Drew Adams wrote:
> (Please use plain-text for this mailing list from now on. I won't bother trying to convert the formatting this time.)
> 
>  
> 
>> IIUC, you _cannot_ use `func-arity' to test whether something
> 
>> is a subr.
> 
> Yes, but you can and should use `subrp' for that.
> 
> Of course you can. But that's irrelevant here. This is about what `subr-arity' does. Its behavior is not the same as `func-arity' for a non-subr. If we are not deprecating `subr-arity' then it is not enough to send users to the doc for `func-arity'.
> 
>> IOW, I am repeating the same argument I made before, when
> 
>> I said that `subr-arity' should not be deprecated and
> 
>> simply replaced by `func-arity'.
> 
> I understood it as argument against aliasing `subr-arity' to
> 
> the new function: this can break _existing_ code if it relies
> 
> on the fact that `subr-arity' signals an error when called with
> 
> anything, but builtin.
> 
> Yes. And?
> 
> The same argument applies to just having its doc string tell users to use `func-arity'. That might not break existing code, but it breaks the doc string. It no longer says what function `subr-arity' does. And it gives the false impression that `func-arity' does the same thing. In fact, `func-arity' does something /different/ if the arg is not a subr.
> 
>> This is a step backward.  Unless we are really deprecating
> 
>> and replacing it, we should document `subr-arity' properly,
> 
>> as before, with the addition of cross-ref to see `func-arity',
> 
>> stating that it handles any type of function.
> 
> I personally don't see why we need two functions for this.
> 
> So, I would deprecate `subr-arity', but keep it around for
> 
> backward compatibility.
> 
> I personally feel the opposite - see my argument about not breaking existing code. But if that will be the decision then that's different. So far, there has been no decision to deprecate `subr-arity', AFAIK.
> 
> On the other hand, I don't really care. All I want is that there
> 
> is `func-arity' that works for _any_ function. I'm not attached
> 
> to anything in the patch and as long as `func-arity' works,
> 
> `func-arity' works for any function. So your want is satisfied.
> 
> However, obtaining your want at the expense of also breaking a doc string is not right.
> 
> feel free to change anything.
> 
> I'm not going to change the broken doc string. But I certainly hope that someone will.
> 
> When code is improved, if that change entails needing to change the doc, then the doc should be updated appropriately.
> 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

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

* RE: Arbitrary function: find the number(s) of expected arguments
  2016-03-25 17:39                     ` Arbitrary function: find the number(s) of expected arguments Eli Zaretskii
@ 2016-03-25 18:31                       ` Drew Adams
  0 siblings, 0 replies; 60+ messages in thread
From: Drew Adams @ 2016-03-25 18:31 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: pogonyshev, emacs-devel

> > >  `subr-arity' is still in the documentation, but I replaced its
> > > description with an advice to use `func-arity' instead.
> >
> > Again, that is wrong, IMO.
> 
> It isn't.

It is, IMO.

> > IIUC, you _cannot_ use `func-arity' to test whether something
> > is a subr.
> 
> You have subrp for that; using subr-arity for this purpose borders
> on the ridiculous.  It's certainly unclean.

Doesn't matter.  The doc string of `subr-arity' should be faithful
to what it does.  Unless we deprecate it in favor of `func-arity',
it is incorrect to pretend that the doc for `func-arity' describes
the behavior of `subr-arity'.

I never said that one should use `subr-arity' in place of `subrp'.
I said only that `func-arity' does not give you the behavior of
`subr-arity'.  The doc for the latter should say what it does.

> > IOW, I am repeating the same argument I made before, when
> > I said that `subr-arity' should not be deprecated and
> > simply replaced by `func-arity'.
> 
> You were wrong then, and you are wrong now.

You were wrong then, and you are wrong now.  Naahhh.

Deprecate `subr-arity' and there will be no problem with having
the doc string of `subr-arity' just send users to the doc of
`func-arity'.

In that case, the only concern is breaking existing code, but
that is often the case when deprecating something.  Just
deprecate it - no problem.

> > If my argument is being rejected (in effect - in the new doc
> > string) then why are we not doing that openly (deprecating
> > `subr-arity' and replacing it with `func-arity')?
> 
> Because you objected, and I'd rather not start yet another
> endless discussion.

No, I did _not_ object to deprecating it.  I only pointed out
that you were wrong in saying that by aliasing you would be
providing backward compatibility.  This is what I said:

  Ignoring all the rest...

(NB that part. I pointed to a problem with _one_ thing you said.)

  This sounds wrong to me.  Just calling the new code (which I
  have not looked at, but which I presume does for arbitrary
  functions what `subr-arity' does for primitives) would NOT
  provide backward compatibility, precisely because it would
  (presumably) NOT have the same behavior as `subr-arity' for
  non-primitives - it would not raise an error.

And that is the case.  Just aliasing to `func-arity' would NOT
provide backward compatibility.  Nothing wrong with deprecating
`subr-arity'.  What was wrong was your claim that aliasing will
provide backward compatibility.



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

* Use plain-text for mail [was: Arbitrary function: find the number(s) of expected arguments]
  2016-03-25 18:28                         ` Clément Pit--Claudel
@ 2016-03-25 18:51                           ` Drew Adams
  2016-03-25 18:57                             ` Use plain-text for mail [ Lars Magne Ingebrigtsen
  2016-03-26  1:12                             ` Use plain-text for mail [was: Arbitrary function: find the number(s) of expected arguments] Yuri Khan
  0 siblings, 2 replies; 60+ messages in thread
From: Drew Adams @ 2016-03-25 18:51 UTC (permalink / raw)
  To: Clément Pit--Claudel, emacs-devel

> Drew, your message is actually much harder to read in Thunderbird that the
> one you replied to. Maybe using a plain text response in such cases would be
> best?

Certainly it would be.  That's why I spent several minutes reformatting
Paul's first message I replied to.  It's one reason we have used plain
text for this mailing list.  I would hope that we continue to do that.

> I had no trouble reading Paul's email, but yours was tricky.

Sorry about that.  I didn't have 20 minutes extra to reformat Paul's
message to (clear) plain text.  Had I just converted it to plain
text without bothering to reformat (to indicate who said what etc.),
it would have been even more unreadable, I'm sure.

You apparently had no problem reformatting for plain text, however.
Perhaps Thunderbird does that better than my mail client.

Wrt the clarity of HTML messages: It depends on your mail client,
the fonts you have available, etc.  Again, a reason to use only
plain text for this list.

FWIW, I use HTML mail maybe 95% of the time at work, as do all
of my colleagues.  And that includes for mailing-list discussions
that involve long threads with deeply nested quotation.  No one
has a problem with this, even though we use different mail clients
etc.  But top-posting is typical - the style is different from here.

I use plain text and bottom-post for mailing lists like this one.

> On 03/25/2016 02:19 PM, Drew Adams wrote:
>
> > (Please use plain-text for this mailing list from now on.
> > I won't bother trying to convert the formatting this time.)

Hopefully this exchange will motivate some of the few who still
use HTML here to use only plain text from now on.



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

* Re: Use plain-text for mail [
  2016-03-25 18:51                           ` Use plain-text for mail [was: Arbitrary function: find the number(s) of expected arguments] Drew Adams
@ 2016-03-25 18:57                             ` Lars Magne Ingebrigtsen
  2016-03-25 19:49                               ` Andreas Schwab
  2016-03-26  1:12                             ` Use plain-text for mail [was: Arbitrary function: find the number(s) of expected arguments] Yuri Khan
  1 sibling, 1 reply; 60+ messages in thread
From: Lars Magne Ingebrigtsen @ 2016-03-25 18:57 UTC (permalink / raw)
  To: Drew Adams; +Cc: Clément Pit--Claudel, emacs-devel

Drew Adams <drew.adams@oracle.com> writes:

> Hopefully this exchange will motivate some of the few who still
> use HTML here to use only plain text from now on.

Or change to a mail user agent that formats HTML mails sensibly and
allows you to respond to them as plain text without the user having to
do any work.  :-)

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no



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

* Re: Use plain-text for mail [
  2016-03-25 18:57                             ` Use plain-text for mail [ Lars Magne Ingebrigtsen
@ 2016-03-25 19:49                               ` Andreas Schwab
  0 siblings, 0 replies; 60+ messages in thread
From: Andreas Schwab @ 2016-03-25 19:49 UTC (permalink / raw)
  To: Lars Magne Ingebrigtsen
  Cc: Clément Pit--Claudel, Drew Adams, emacs-devel

Lars Magne Ingebrigtsen <larsi@gnus.org> writes:

> Drew Adams <drew.adams@oracle.com> writes:
>
>> Hopefully this exchange will motivate some of the few who still
>> use HTML here to use only plain text from now on.
>
> Or change to a mail user agent that formats HTML mails sensibly and
> allows you to respond to them as plain text without the user having to
> do any work.  :-)

(add-to-list 'mm-discouraged-alternatives "text/html") saves you a lot
of hair.

Andreas.

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."



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

* Re: Use plain-text for mail [was: Arbitrary function: find the number(s) of expected arguments]
  2016-03-25 18:51                           ` Use plain-text for mail [was: Arbitrary function: find the number(s) of expected arguments] Drew Adams
  2016-03-25 18:57                             ` Use plain-text for mail [ Lars Magne Ingebrigtsen
@ 2016-03-26  1:12                             ` Yuri Khan
  1 sibling, 0 replies; 60+ messages in thread
From: Yuri Khan @ 2016-03-26  1:12 UTC (permalink / raw)
  To: Drew Adams; +Cc: Clément Pit--Claudel, Emacs developers

On Sat, Mar 26, 2016 at 12:51 AM, Drew Adams <drew.adams@oracle.com> wrote:

> You apparently had no problem reformatting for plain text, however.
> Perhaps Thunderbird does that better than my mail client.

Perhaps that is a bug in your mail client.


>> > (Please use plain-text for this mailing list from now on.
>> > I won't bother trying to convert the formatting this time.)
>
> Hopefully this exchange will motivate some of the few who still
> use HTML here to use only plain text from now on.

I agree that plain text is better for mailing lists, but you have to
account for the fact that several mail clients don’t even bother
supporting plain text mail or including a text/plain MIME part for
HTML emails any more.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-25 16:16                 ` Paul Pogonyshev
  2016-03-25 16:35                   ` Drew Adams
@ 2016-03-26  8:27                   ` Eli Zaretskii
  2016-03-26 11:42                     ` Paul Pogonyshev
  1 sibling, 1 reply; 60+ messages in thread
From: Eli Zaretskii @ 2016-03-26  8:27 UTC (permalink / raw)
  To: Paul Pogonyshev; +Cc: emacs-devel

> Date: Fri, 25 Mar 2016 17:16:42 +0100
> From: Paul Pogonyshev <pogonyshev@gmail.com>
> Cc: emacs-devel@gnu.org
> 
>  If we are going to keep sub-arity, I'd prefer if this new function
>  called it, instead of copying its code inline.
> 
> Done.
> 
>  Also, I believe you said you'd write the documentation? Could you
>  please add that? Then the patch will be ready to go in, I think.
> 
> In the attached patch I modified `doc/lispref/functions.texi': text about
> `subr-arity' is moved to a new section above about `func-arity' and
> adapted as needed. `subr-arity' is still in the documentation, but I
> replaced its description with an advice to use `func-arity' instead. Is
> that enough?

It's enough, but in the future please also provide a NEWS entry, like
the one I added.

> Do you still need changelog entries? Long time since I committed
> anything to Emacs, maybe you finally got rid of them (I hope)?

We no longer maintain ChangeLog files in the repository, but we do
request commit log messages in the ChangeLog format, so please do
provide them in the future.  (I wrote them for you this time.)

I pushed the changes to the master branch, thanks.

There's one potential issue left:

 (func-arity 'with-temp-buffer) => error-> Invalid function: with-temp-buffer

Is it possible to support macros as well?  If not, how about producing
a more meaningful error message?



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-26  8:27                   ` Eli Zaretskii
@ 2016-03-26 11:42                     ` Paul Pogonyshev
  2016-04-02  9:48                       ` Eli Zaretskii
  0 siblings, 1 reply; 60+ messages in thread
From: Paul Pogonyshev @ 2016-03-26 11:42 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

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

Eli Zaretskii wrote:
> > In the attached patch I modified `doc/lispref/functions.texi': text
about
> > `subr-arity' is moved to a new section above about `func-arity' and
> > adapted as needed. `subr-arity' is still in the documentation, but I
> > replaced its description with an advice to use `func-arity' instead. Is
> > that enough?
>
> It's enough, but in the future please also provide a NEWS entry, like
> the one I added.
>
> > Do you still need changelog entries? Long time since I committed
> > anything to Emacs, maybe you finally got rid of them (I hope)?
>
> We no longer maintain ChangeLog files in the repository, but we do
> request commit log messages in the ChangeLog format, so please do
> provide them in the future.  (I wrote them for you this time.)

OK.

> I pushed the changes to the master branch, thanks.

Thank you.

> There's one potential issue left:
>
>  (func-arity 'with-temp-buffer) => error-> Invalid function:
with-temp-buffer
>
> Is it possible to support macros as well?  If not, how about producing
> a more meaningful error message?

Well, according to `functionp' macros are not functions, so it's
sort of correct.  On the other hand, it seems (symbol-function ...)
works, returning ('macro . FUNCTION) for them.  However, I can't
find any real C-level way to handle macros, e.g. `macrop' is even
not a builtin.

Or is just treating them as lists fine?  If so, we could add
something like this (untested):

diff --git a/src/eval.c b/src/eval.c
index 64a6655..5c594b8 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -2958,6 +2958,9 @@ function with `&rest' args, or `unevalled' for a
special form.  */)
       && (function = XSYMBOL (function)->function, SYMBOLP (function)))
     function = indirect_function (function);

+  if (CONSP (function) && EQ (XCAR (function), Qmacro))
+    function = XCDR (function);
+
   if (SUBRP (function))
     result = Fsubr_arity (function);
   else if (COMPILEDP (function))

However, I'm not exactly sure what will happen with autoloaded
macros.

Paul

[-- Attachment #2: Type: text/html, Size: 2909 bytes --]

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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 19:52         ` Stefan Monnier
  2016-03-19 20:33           ` Eli Zaretskii
@ 2016-03-26 15:55           ` Elias Mårtenson
  2016-03-26 17:20             ` Stefan Monnier
  1 sibling, 1 reply; 60+ messages in thread
From: Elias Mårtenson @ 2016-03-26 15:55 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

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

On 20 Mar 2016 03:53, "Stefan Monnier" <monnier@iro.umontreal.ca> wrote:
>
> As mentioned to my reply in the corresponding thread, you're better off
> just making the call and catching the potential
> `wrong-number-of-arguments' signal.

Isn't that really dangerous? You're going to catch that error from any
function call, not just the top one that you are interested in. Sounds like
something that would be he'll to debug if it hit you.

Regards,
Elias

[-- Attachment #2: Type: text/html, Size: 621 bytes --]

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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-26 15:55           ` Elias Mårtenson
@ 2016-03-26 17:20             ` Stefan Monnier
  0 siblings, 0 replies; 60+ messages in thread
From: Stefan Monnier @ 2016-03-26 17:20 UTC (permalink / raw)
  To: Elias Mårtenson; +Cc: emacs-devel

>> As mentioned to my reply in the corresponding thread, you're better off
>> just making the call and catching the potential
>> `wrong-number-of-arguments' signal.
> Isn't that really dangerous? You're going to catch that error from any
> function call, not just the top one that you are interested in.  Sounds like
> something that would be he'll to debug if it hit you.

That error should normally never be signaled, so the likelihood of there
being a problem is extremely small.  I've never had trouble with it,
including when debugging things.  Even when debugging
wrong-number-of-arguments errors, this error has always been re-signaled
in the "alternative calling case", so it never ended up hiding
a real problem (and even less so causing such a problem).

Of course, in theory it can.  But then, in theory testing the function's
arity can also fail in all kinds of ways.


        Stefan



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-26 11:42                     ` Paul Pogonyshev
@ 2016-04-02  9:48                       ` Eli Zaretskii
  0 siblings, 0 replies; 60+ messages in thread
From: Eli Zaretskii @ 2016-04-02  9:48 UTC (permalink / raw)
  To: Paul Pogonyshev; +Cc: emacs-devel

> Date: Sat, 26 Mar 2016 12:42:17 +0100
> From: Paul Pogonyshev <pogonyshev@gmail.com>
> Cc: emacs-devel@gnu.org
> 
> > There's one potential issue left:
> >
> >  (func-arity 'with-temp-buffer) => error-> Invalid function: with-temp-buffer
> >
> > Is it possible to support macros as well?  If not, how about producing
> > a more meaningful error message?
> 
> Well, according to `functionp' macros are not functions, so it's
> sort of correct.  On the other hand, it seems (symbol-function ...)
> works, returning ('macro . FUNCTION) for them.  However, I can't
> find any real C-level way to handle macros, e.g. `macrop' is even
> not a builtin.
> 
> Or is just treating them as lists fine?  If so, we could add
> something like this (untested):
> 
> diff --git a/src/eval.c b/src/eval.c
> index 64a6655..5c594b8 100644
> --- a/src/eval.c
> +++ b/src/eval.c
> @@ -2958,6 +2958,9 @@ function with `&rest' args, or `unevalled' for a
> special form.  */)
>        && (function = XSYMBOL (function)->function, SYMBOLP (function)))
>      function = indirect_function (function);
> 
> +  if (CONSP (function) && EQ (XCAR (function), Qmacro))
> +    function = XCDR (function);
> +
>    if (SUBRP (function))
>      result = Fsubr_arity (function);
>    else if (COMPILEDP (function))
> 
> However, I'm not exactly sure what will happen with autoloaded
> macros.

Thanks, I installed the above.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 19:21             ` Drew Adams
@ 2016-04-18 18:43               ` Davis Herring
  0 siblings, 0 replies; 60+ messages in thread
From: Davis Herring @ 2016-04-18 18:43 UTC (permalink / raw)
  To: Drew Adams, Eli Zaretskii, Paul Pogonyshev; +Cc: emacs-devel

[I'm back from vacation; a few already-old threads caught my eye.]

> Any existing code that depends on an error being raised by
> `subr-arity' would break.  E.g.,
>
> (condition-case err
>      (subr-arity 'foo)
>    (error (do-something)))

This issue is that of undefined behavior: is (subr-arity 0) UB or is it 
defined to be an error?  The doc string says simply "SUBR must be a 
built-in function." which is language typical of UB in some contexts, 
but Emacs doesn't explicitly have that notion to my knowledge.

If it's UB, then your example is wrong already and its breakage is 
irrelevant.  If the error is a defined part of the interface, then yes 
the change is incompatible.  All kinds of interfaces are thoroughly 
defined only for a subset of cases, and it's as much as anything a 
cultural issue whether the (historical) implementation's behavior 
outside that set is sacrosanct or not.

Davis

-- 
This product is sold by volume, not by mass.  If it appears too dense or 
too sparse, it is because mass-energy conversion has occurred during 
shipping.



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

* Re: Arbitrary function: find the number(s) of expected arguments
  2016-03-19 15:43               ` Eli Zaretskii
  2016-03-19 15:57                 ` Michael Heerdegen
  2016-03-19 16:14                 ` Philipp Stephani
@ 2016-04-18 23:02                 ` Davis Herring
  2 siblings, 0 replies; 60+ messages in thread
From: Davis Herring @ 2016-04-18 23:02 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Michael Heerdegen, emacs-devel

[I'm back from vacation; a few already-old threads caught my eye.]

>> (defun f (a b) (list a b))
>>
>> (defalias 'g (apply-partially #'f 1))

[...]

> But Emacs itself clearly _knows_ that only one argument is acceptable.
> So a function that replicates the steps made by the Lisp interpreter
> to arrive at this conclusion will be able to reach the same
> conclusion.  So I don't see any insoluble problems here.

Others have provided concrete examples already, but the point is that 
there's no clear distinction between "the wrapper(s)" and "the real 
code" so that `func-arity' could stop when it finished the one. 
Checking that the actual function-call step of evaluation (substitution 
of arguments, etc.) could succeed is not what is wanted in practice 
given functions like `apply-partially'.

Annotations could theoretically be added to indicate the boundary, but 
making them useful in the absence of evaluation would be non-trivial. 
(Even annotations that only worked during a true call could be useful to 
avoid ambiguities like those that inspire the ugly NotImplemented 
convention in Python.)

Lacking such wrapper analysis, if we accept the implementations of 
`apply-partially' and similar even though the function objects they 
return have the wrong signatures, we are making the statement that 
function signatures mediate between the Lisp programmer and the 
evaluator, not between programmers.  (Pretend that all functions are 
written "(defun pkg-f (&rest args) (apply 'pkg--f-impl args))".)  Put 
differently, functions as objects do not expose a "what is my interface" 
interface, but only the function call interface (buyer beware).  This 
interface-blindness can be useful: we _know_ that it makes no difference 
to the client that passes 2 arguments that it could have provided a 3rd 
&optional one.

One may choose instead to say that function signatures should be an 
accessible attribute.  But then `apply-partially' must be rewritten to 
examine the signature of the wrapped function (whose definition might 
change!) and to arrange to expose the correctly-reduced signature.  And 
we have to decide whether the signature data-type supported by the 
evaluator (x required + y optional arguments, where y may be infinite) 
is the one we wish to bless -- what if a function accepted any even 
number of arguments, for example?

Davis

-- 
This product is sold by volume, not by mass.  If it appears too dense or 
too sparse, it is because mass-energy conversion has occurred during 
shipping.



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

end of thread, other threads:[~2016-04-18 23:02 UTC | newest]

Thread overview: 60+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-03-15 18:48 Arbitrary function: find the number(s) of expected arguments Paul Pogonyshev
2016-03-15 22:45 ` Davis Herring
2016-03-16  7:41   ` Paul Pogonyshev
2016-03-19 12:26     ` Paul Pogonyshev
2016-03-19 13:10       ` Eli Zaretskii
2016-03-19 13:42         ` Paul Pogonyshev
2016-03-19 13:54         ` Michael Heerdegen
2016-03-19 14:08           ` Eli Zaretskii
2016-03-19 15:20             ` Michael Heerdegen
2016-03-19 15:43               ` Eli Zaretskii
2016-03-19 15:57                 ` Michael Heerdegen
2016-03-19 16:24                   ` Eli Zaretskii
2016-03-19 17:43                     ` Michael Heerdegen
2016-03-19 17:50                       ` Eli Zaretskii
2016-03-19 17:59                         ` Michael Heerdegen
2016-03-19 18:14                           ` Eli Zaretskii
2016-03-19 16:14                 ` Philipp Stephani
2016-03-19 16:27                   ` Michael Heerdegen
2016-03-19 16:27                   ` Eli Zaretskii
2016-03-19 16:30                     ` Philipp Stephani
2016-03-19 16:32                       ` Eli Zaretskii
2016-03-19 16:34                         ` Philipp Stephani
2016-03-19 16:46                           ` Philipp Stephani
2016-03-19 16:47                           ` Eli Zaretskii
2016-03-19 17:16                             ` Philipp Stephani
2016-03-19 17:48                               ` Eli Zaretskii
2016-03-19 17:49                                 ` Philipp Stephani
2016-03-19 18:11                                   ` Eli Zaretskii
2016-03-19 18:35                                     ` Michael Heerdegen
2016-04-18 23:02                 ` Davis Herring
2016-03-19 19:52         ` Stefan Monnier
2016-03-19 20:33           ` Eli Zaretskii
2016-03-19 22:43             ` Stefan Monnier
2016-03-26 15:55           ` Elias Mårtenson
2016-03-26 17:20             ` Stefan Monnier
2016-03-19 14:26       ` Philipp Stephani
2016-03-19 16:51         ` Paul Pogonyshev
2016-03-19 18:09           ` Eli Zaretskii
2016-03-19 19:32             ` Michael Heerdegen
2016-03-19 19:39               ` Eli Zaretskii
2016-03-19 20:59                 ` Michael Heerdegen
2016-03-21 18:36             ` Paul Pogonyshev
2016-03-25  8:44               ` Eli Zaretskii
2016-03-25 16:16                 ` Paul Pogonyshev
2016-03-25 16:35                   ` Drew Adams
2016-03-25 17:16                     ` Paul Pogonyshev
2016-03-25 18:19                       ` Drew Adams
2016-03-25 18:28                         ` Clément Pit--Claudel
2016-03-25 18:51                           ` Use plain-text for mail [was: Arbitrary function: find the number(s) of expected arguments] Drew Adams
2016-03-25 18:57                             ` Use plain-text for mail [ Lars Magne Ingebrigtsen
2016-03-25 19:49                               ` Andreas Schwab
2016-03-26  1:12                             ` Use plain-text for mail [was: Arbitrary function: find the number(s) of expected arguments] Yuri Khan
2016-03-25 17:39                     ` Arbitrary function: find the number(s) of expected arguments Eli Zaretskii
2016-03-25 18:31                       ` Drew Adams
2016-03-26  8:27                   ` Eli Zaretskii
2016-03-26 11:42                     ` Paul Pogonyshev
2016-04-02  9:48                       ` Eli Zaretskii
     [not found]           ` <<83y49e731p.fsf@gnu.org>
2016-03-19 19:21             ` Drew Adams
2016-04-18 18:43               ` Davis Herring
2016-03-16  3:47 ` Stefan Monnier

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).