3

I'm looking for a way to get the amount of arguments of a function, a function like the imaginary length-args function below:

(defun func1 (arg1 arg2) 
  ())

(defun get-count-args-func (func) 
  (length-args func))

(get-count-args-func #'func1)
;; return 2

In practice what I really need is to solve a question from my test library that I am bringing from javascript to lisp, where I deal both with synchronous and asynchronous tests, where I need to do something like this(simplifying to the maximum the reality):

(defun test () 
  ;;...
  )

(defun done ()
  ;;...
  )

(defun run-test (test)
  (if (>= (get-count-args-func test) 1)
      (test (funcall done))
      (test)))
Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
PerduGames
  • 1,108
  • 8
  • 19
  • @Rainer no, she does not have an answer there, she has the way there. People searched differently, and with the key words from the other question they possibly did not find the question that provides the path and would end up missing a response. So I do not think this is a duplicate, but a different question that is based on another answer, that's different things, possibly you would not find the other question if I had not linked it here. – PerduGames Mar 09 '19 at 12:34
  • 1
    you are asking for the arity of a function, that's what the other question is about and you literally copied the code from one of the answers to your own answer here. – Rainer Joswig Mar 09 '19 at 12:44
  • @Rainer The word "arity" does not come to my head what I was looking for and for many people it will not come either. And yes, so I provide the link showing where is who wrote the code, it's not a copy, it's a quote, it's different things, do not confuse the terms; but do how you want, you're the king. – PerduGames Mar 09 '19 at 12:53
  • @RainerJoswig I've learned from programming that it's interesting to uncouple things and what you're trying to do by marking this question as duplicate is to couple things, see why: the other question says "Find Function's Arithmetic in common lisp", anyone who searches for it will find, while my question says "How to get the amount of arguments of a function", but both are with the same answer, right, – PerduGames Mar 09 '19 at 13:39
  • @RainerJoswig but let's say, in ten years, Common Lisp specifies a new `length- args-function` or `length-arglist` for all implementations, if my question still exists, a new response can be added to it, but if it does not exist, the question "...Arity..." can be updated alsowhith a new standard for an `arglist` function, but you may lose information that there is also a `length-args-function` function. Yet this is very vague and distant, but it is a thought that came to me, yet who am I to judge what is or is not duplicated, I hardly know if I am a copy or not, then it is up to you. – PerduGames Mar 09 '19 at 13:40
  • 1
    That exactly the same question gets posted with different wordings is not unusual. It then can get marked as duplicate. Your question is not going away: it stays and people can find it. But there is very little use for exactly the same question to have multiple versions of the same answer in different places. – Rainer Joswig Mar 09 '19 at 15:42
  • It seems that the question you have asked (how do you determine the arithmetic of a function) is not actually the question you want the answer to (how to tell if a function is asynchronous). Please try to ask your actual question on stack overflow instead of a question derived from your attempted solution because otherwise the answer to the question you ask may not help much with your problem. Here are some suggestions that may actually help: 1. Maybe use promises (ie return values you can inspect) instead of callbacks; 2. Set a symbol property when defining asynchronous functions; ... – Dan Robertson Mar 09 '19 at 18:58
  • ... 3. Have asynchronous functions be funcallable objects which may be inspected; 4. Have the test framework set up in a way that you easily specify whether the tests are asynchronous (e.g. `(defmacro deftest ...)`); 5. Have all tests be asynchronous, just have the synchronous ones be trivial. – Dan Robertson Mar 09 '19 at 19:00
  • @DanRobertson I'm not sure about what you said, the question is about "get the amount of arguments of a function", no matter where I would use it, because I wanted to know how to do that, one day I might need it. Where did I use it? so here is my javascript test library which I'm bringing to lisp: https://github.com/perdugames/cacau#async-test, I do not need to know if a function is synchronous or not, you can even use `done` it in sync tests if you want. – PerduGames Mar 11 '19 at 00:33
  • @DanRobertson If you want to delve a little deeper, here it is: https://github.com/noloop/cacau/blob/master/test/async-runner.lisp an asynchronous runner that I wrote quick to test the library of tests that I am building . – PerduGames Mar 11 '19 at 00:36

2 Answers2

2

I found a way to resolve this issue by looking at this answer here: Find Function's Arity in common lisp, an arglist function is required to handle the various implementations, see the implementation of arglist:

;; function provided by @sds at https://stackoverflow.com/questions/15465138/find-functions-arity-in-common-lisp
(defun arglist (fn)
  "Return the signature of the function."
  #+allegro (excl:arglist fn)
  #+clisp (sys::arglist fn)
  #+(or cmu scl)
  (let ((f (coerce fn 'function)))
    (typecase f
      (STANDARD-GENERIC-FUNCTION (pcl:generic-function-lambda-list f))
      (EVAL:INTERPRETED-FUNCTION (eval:interpreted-function-arglist f))
      (FUNCTION (values (read-from-string (kernel:%function-arglist f))))))
  #+cormanlisp (ccl:function-lambda-list
                (typecase fn (symbol (fdefinition fn)) (t fn)))
  #+gcl (let ((fn (etypecase fn
                    (symbol fn)
                    (function (si:compiled-function-name fn)))))
          (get fn 'si:debug))
  #+lispworks (lw:function-lambda-list fn)
  #+lucid (lcl:arglist fn)
  #+sbcl (sb-introspect:function-lambda-list fn)
  #-(or allegro clisp cmu cormanlisp gcl lispworks lucid sbcl scl)
  (error 'not-implemented :proc (list 'arglist fn)))

Now I can do:

(defun func1 (arg1 arg2) 
  ())

(defun get-count-args-func (func) 
  (length (arglist func)))

(get-count-args-func #'func1) => 2

So I have the number of arguments, you just have to be attentive, if you have some &key argument, &rest or &optional, for example:

(defun func1 (&optional arg1 arg2) 
  ())

(defun get-count-args-func (func) 
  (length (arglist func)))

(get-count-args-func #'func1) => 3

See that you get 3, because what arglist function is returning:

(&OPTIONAL ARG1 ARG2)

while only with required arguments he returned:

(ARG1 ARG2)
PerduGames
  • 1,108
  • 8
  • 19
-1

The most simple way to do this would be to add rest parameters to your function and within the function use the built-in length function to determine the number of args the function takes:

CL-USER> (defun fun-1(&rest args)
   (length args))
FUN-1
CL-USER> (fun-1 1 2 3)
3
Simeon Ikudabo
  • 2,152
  • 1
  • 10
  • 27
  • This is not an option for the case I had to deal with, because sometimes I pass a "done" argument, sometimes no, since I am dealing with both synchronous and asynchronous functions, asynchronous require a "done" argument and synchronous not require. – PerduGames Mar 09 '19 at 11:01
  • And my search is not how many arguments were passed, but how many arguments a function is asking, `get-count-args-func` would be a meta-function because it's dealing with function meta information. – PerduGames Mar 09 '19 at 11:08
  • I updated my question, see. – PerduGames Mar 09 '19 at 12:43