Skip to content

findGlobals(): Distinguish between functions and non-functions #95

@HenrikBengtsson

Description

@HenrikBengtsson

Below is an example, where a local numeric variable sum is picked up by globalsOf(), despite the expression uses a function sum().

sum <- 42
expr <- quote( sum(1:10) )

names <- globals::findGlobals(expr)
print(names)
#> "{"   "sum" ":"

objs <- globals::globalsOf(expr)
str(objs)
#> List of 3
#>  $ {  :.Primitive("{") 
#>  $ sum: num 42
#>  $ :  :.Primitive(":") 
#>  - attr(*, "where")=List of 3
#>   ..$ {  :<environment: base> 
#>   ..$ sum:<environment: R_GlobalEnv> 
#>   ..$ :  :<environment: base> 
#>  - attr(*, "class")= chr [1:2] "Globals" "list"

Ideally, globalsOf() would pick up base::sum() instead. For that to happen, we need to refine globals::findGlobals() to record whether symbol sum refers to a function or not. That could be achieved by findGlobals() inspect the language object that sum is part of. For instance, from:

expr <- quote( sum(1:10) )
str(list(first = expr[[1]], class = class(expr), typeof = typeof(expr), length = length(expr)))
#> List of 4
#>  $ first : symbol sum
#>  $ class : chr "call"
#>  $ typeof: chr "language"
#>  $ length: int 2

expr <- quote( sum )
str(list(class = class(expr), typeof = typeof(expr), length = length(expr)))
#> List of 3
#>  $ class : chr "name"
#>  $ typeof: chr "symbol"
#>  $ length: int 1

## But it's not that easy

expr <- quote( sum + 2 )
str(list(first = expr[[1]], class = class(expr), typeof = typeof(expr), length = length(expr)))
#> List of 4
 $ first : symbol +
#>  $ class : chr "call"
#>  $ typeof: chr "language"
#>  $ length: int 3
 
expr <- quote( -sum )
str(list(first = expr[[1]], class = class(expr), typeof = typeof(expr), length = length(expr)))
#> List of 4
 $ first : symbol -
#>  $ class : chr "call"
#>  $ typeof: chr "language"
#>  $ length: int 2

we see that we can infer when symbol sum is the name of a function call or not.

One could have findGlobals(expr, annotate = TRUE) return this information as well, e.g. as attributes. But, is that shoehorning the original design of globals to much, which is basically an enhancement of codetools? It would be worth investigating if there are better AST tools for what's trying to be achieved. OTH, the above improvement might be good enough for 99% of the use cases.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions