Count number of arguments in function

Hi,

If I want to know how many arguments a function has, i can do something like length(formals(func))

This works for my functions, but not for some base R functions like is.character (perhaps because its compiled?)

Reprex below

# Create a function with 3 arguments
func <- function(a, b, c){
  message(a, b, c)
}

# Can get number of args with length & formals
length(formals(func))
#> [1] 3

# But this doesn't work for certain base R functions
length(formals(is.character))
#> [1] 0

Created on 2023-01-06 with reprex v2.0.2

Is it possible to programmatically get the number of arguments for any R function?

I don't think so; as you've found there is a clear difference between normal functions and primitive functions.
you an test if a function is primitive by is.primitive

> is.primitive(as.character)
[1] TRUE

You could try a hacky approach using string processing ; get hold of the strings like

capture.output(args(as.character))[[1]] 

but there are lots of interesting edge cases even with normal functions.

length(fn_fmls(mean))
# 2

How do you intend to interpret ... ?
as 1 or as Infinite ?

the following code runs in R

mean((1:100)^2,na.rm=TRUE,trim=1,random_does_nothing=5)
1 Like

You raise some interesting points @nirgrahamuk. Thanks for your response!

I'm interested in writing functions that assert basic features of functions

Your insights make me think two basic functions need to be available:

  1. a function that returns a count of named arguments,
  2. a function that returns a boolean describing whether dots ... are used as an argument

From these you could build other functions that assert there are 'at least X' arguments. Then I'd leave it up to the end user whether ... should count as Infinite (default yes), or perhaps just throw an error (think function that takes a very specific 2 argument function the user will likely have to create - here asserting exactly 2 named arguments and ignoring or throwing an error if ... is present seems perfectly reasonable.

I think having some 'ellipses_value' argument that could be 1 or Inf , might push users of these functions (i.e. future me :joy:) to think about exactly what they want to assert.

I will leave this topic open another couple of days on the off chance anyone has a less hacky way to handle primitives.

Thanks again for your answer!!

oh, it seems that there is a way to get a common result from primitive and non-primitive functions.

formals(args(as.character))
length(formals(args(as.character)))
formals(args(mean))
length(formals(args(mean))
1 Like

I think you've cracked it @nirgrahamuk!

We can additionally get the names simply by adding: names(formals(args(as.character))).

# Get argument names (Primitives)
names(formals(args(as.character)))
#> [1] "x"   "..."

# Get number of arguments (Primitives)
length(formals(args(as.character)))
#> [1] 2

# Get argument names (Non-Primitive)
names(formals(args(mean)))
#> [1] "x"   "..."

# Get number of arguments (Non-Primitive)
length(formals(args(mean)))
#> [1] 2

Created on 2023-01-07 with reprex v2.0.2

Thats most of the info required to build a robust system of assertions on function arguments

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.