Re: [Rd] codetools wrongly complains about lazy evaluation in S4 methods

2023-06-13 Thread Ivan Krylov
On Sat, 3 Jun 2023 11:50:59 -0400
Mikael Jagan  wrote:

>  > setOldClass("qr")
>  > setMethod("qr.X", signature(qr = "qr"), function(qr, complete,
>  > ncol) NULL)  
> 
> The formals of the newly generic 'qr.X' are inherited from the
> non-generic function in the base namespace.  Notably, the inherited
> default value of formal argument 'ncol' relies on lazy evaluation:
> 
>  > formals(qr.X)[["ncol"]]  
>  if (complete) nrow(R) else min(dim(R))
> 
> where 'R' must be defined in the body of any method that might
> evaluate 'ncol'. To my surprise,
> tools:::.check_code_usage_in_package() complains about the undefined
> symbol:
> 
>  qr.X: no visible binding for global variable 'R'
>  qr.X,qr: no visible binding for global variable 'R'
>  Undefined global functions or variables:
>R

In other words, codetools::checkUsage(base::qr.X) says nothing while
codetools::checkUsage(TestPackage::qr.X) complains. I think the
difference is that codetools::findFuncLocals sees an assignment to `R`
in the body of base::qr.X:

codetools::findFuncLocals(formals(base::qr.X), body(base::qr.X))
# [1] "cmplx"   "cn"  "ip"  "p"   "pivoted" "R"   "res"
# [8] "tmp"

The problem, then, is that an S4 generic shouldn't be having such
assignments in its body. One way to fix this would be to modify
codetools::checkUsage to immediately return if inherits(fun,
'standardGeneric'), but I don't know enough about S4 to say whether
this is safe. (A more comprehensive fix would be to check every
encountered method against the formals of the generic, but that sounds
complicated.) Arguably, static analysis will always be wrong about
something, so we're trading a false positive for potential false
negatives.

-- 
Best regards,
Ivan

__
R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel


Re: [Rd] codetools wrongly complains about lazy evaluation in S4 methods

2023-06-13 Thread Kasper Daniel Hansen
On Sat, Jun 3, 2023 at 11:51 AM Mikael Jagan  wrote:

> The formals of the newly generic 'qr.X' are inherited from the non-generic
> function in the base namespace.  Notably, the inherited default value of
> formal argument 'ncol' relies on lazy evaluation:
>
>  > formals(qr.X)[["ncol"]]
>  if (complete) nrow(R) else min(dim(R))
>
> where 'R' must be defined in the body of any method that might evaluate
> 'ncol'.
>

Perhaps I am misunderstanding something, but I think Mikael's expectations
about the scoping rules of R are wrong.  The enclosing environment of ncol
is where it was _defined_ not where it is _called_ (apologies if I am
messing up the computer science terminology here).

This suggests to me that codetools is right.  But a more extended example
would be useful. Perhaps there is something special with setOldClass()
which I am no aware of.

Also, Bioconductor has 100s of packages with S4 where codetools works well.

Kasper

[[alternative HTML version deleted]]

__
R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel


Re: [Rd] codetools wrongly complains about lazy evaluation in S4 methods

2023-06-13 Thread Simon Urbanek
I agree that this is not an R issue, but rather user error of not defining a 
proper generic so the check is right. Obviously, defining a generic with 
implementation-specific ncol default makes no sense at all, it should only be 
part of the method implementation. If one was to implement the same default 
behavior in the generic itself (not necessarily a good idea) the default would 
be ncol = if (complete) nrow(qr.R(qr, TRUE)) else min(dim(qr.R(qr, TRUE))) to 
not rely on the internals of the implementation.

Cheers,
Simon


> On 14/06/2023, at 6:03 AM, Kasper Daniel Hansen 
>  wrote:
> 
> On Sat, Jun 3, 2023 at 11:51 AM Mikael Jagan  wrote:
> 
>> The formals of the newly generic 'qr.X' are inherited from the non-generic
>> function in the base namespace.  Notably, the inherited default value of
>> formal argument 'ncol' relies on lazy evaluation:
>> 
>>> formals(qr.X)[["ncol"]]
>> if (complete) nrow(R) else min(dim(R))
>> 
>> where 'R' must be defined in the body of any method that might evaluate
>> 'ncol'.
>> 
> 
> Perhaps I am misunderstanding something, but I think Mikael's expectations
> about the scoping rules of R are wrong.  The enclosing environment of ncol
> is where it was _defined_ not where it is _called_ (apologies if I am
> messing up the computer science terminology here).
> 
> This suggests to me that codetools is right.  But a more extended example
> would be useful. Perhaps there is something special with setOldClass()
> which I am no aware of.
> 
> Also, Bioconductor has 100s of packages with S4 where codetools works well.
> 
> Kasper
> 
>   [[alternative HTML version deleted]]
> 
> __
> R-devel@r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
> 

__
R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel