I've wanted to use specials to detect random effect terms as in ~(f | g),
but
non-syntactic function names (in this case, '|') are currently not
supported.
I proposed a patch some time ago:
https://bugs.r-project.org/show_bug.cgi?id=18568
I wonder if some of these issues with '::' could be solved backwards
compatibly
with a patch along similar lines, i.e., not requiring specials to be syntactic
names, and avoiding doing string comparison with deparse(<language>). One
idea:
* At R level, allow something like
specials=structure(c("cluster", "cluster"), package =
c("", "survival"))
to separately mark as special calls cluster() and survival::cluster() in the
model formula.
* At C level, test for calls whose first component is a symbol (like
'cluster')
or a call to '::' whose two arguments are symbols (like
'survival::cluster'),
matching these against the 'specials', respecting attr(specials,
"package")
if that is a character vector of length equal to length(specials).
where I recommend using attribute 'package' rather than 'names'
because:
* It's not hard to imagine that code "out there" is using
specials=<named character>, and we should not change the behaviour of
that
code.
* Package 'methods' defines formal class 'ObjectsWithPackage'
with the same
layout as well as utilities 'PackageSlot',
'PackageSlot<-', ...
Well, that's just just one idea ... I understand reluctance to make
non-trivial
changes to model.c, but maybe there is a basis here, if people are more and more
(for better or worse (mostly worse)) trying to use '::' everywhere
including in
model formulas.
Mikael
> Date: Wed, 19 Mar 2025 16:49:31 +0000
> From: "Therneau, Terry M., Ph.D."<therneau at mayo.edu>
>
> In response to the tidyverse habit of adding another zillion functions to
one's search space, the use of things like survival::coxph is becoming more
common. But this practice breaks the use of the specials argument in
terms.formula, something that I make heavy use of in the survival package. The
following two pairs of models give different answers, and the second is wrong.
>
> fit1a <- lm(GNP ~ Year + offset(Unemployed), longley)
> fit1b<- lm(GNP ~ Year + stats::offset(Unemployed), longley)
>
> fit1a <- survdiff(Surv(time, status) ~ rx + cluster(litter), rats)
> fit1b <- survdiff(Surv(time, status) ~ rx + survival::cluster(litter),
rats)
> zed <- survival::cluster(rats$litter)
> fit1c <- survdiff(Surv(time, status) ~ rx + zed, rats)
>
> In the most recent CRAN version of survival I added some pre-processing
steps that successfully catch fit1b, by stripping off "survival::"
from formulas before calling terms.formula. But I can't prevent fit1c, and
don't yet know if there are other case not covered by my current hack. At
least one of the CRAN packages that depends on survival has an example of
exactly fit1c in their test suite.
>
> The survival package uses the special argument a lot: strata, cluster,
pspline, tt, frailty, and ridge. I'm trying to think of a good plan for
long term changes. I list 3 below, and am hoping for better ideas or input.
>
> a. Caveat emptor: If you work hard to fool the specials argument, and
succeed, then "Congratulations, you fooled the parser."
>
> b. What I did early on with tt(), which is to make the function defition
completely internal to coxph (the only survival function that uses tt). People
who type survival::tt or survival:::tt get an error message. A plus of this
is that the error message will wean users from pasting survival:: to everything
inside a formula. The disadvantages are first that it will break existing user
code (most of which should be broken -- its not doing what they think), a second
is that there may be use cases for strata, say, outside of a survival formula;
it is essentially factor with shorter default labels.
>
> c. Make all of these functions have a class, and rewrite the code to depend
on the class rather than specials. The Surv function is recognized in this way,
so is not harmed by survival::Surv. It is also why calling Surv to create a new
variable is fine. I'd still retain specials, to support legacy code.
>
> The more ornery part of me votes for b (what is the effect on help files)?
In any case a change won't happen overnight.
> Do we leave offset in the caveat emptor group?
> A small section needs to be added to the "user written packages"
document, where it talks about specials.
> What other packages use specials?
>
> Terry T.