R's default conflict management system gives the most recently loaded package precedence. This can make it hard to detect conflicts, particularly when they arise because a package update creates ambiguity that did not previously exist. 'conflicted' takes a different approach, making every conflict an error and forcing you to choose which function to use.
The goal of conflicted is to provide an alternative conflict resolution strategy. R’s default conflict resolution system gives precedence to the most recently loaded package. This can make it hard to detect conflicts, particularly when introduced by an update to an existing package. conflicted takes a different approach, making every conflict an error and forcing you to choose which function to use.
Thanks to @krlmlr for this neat idea! This code was previously part of the experimental strict package, but I decided improved conflict resolution is useful by itself and worth its own package.
# install.packages("devtools")devtools::install_github("r-lib/conflicted")
To use conflicted, all you need to do is load it:
library(conflicted)library(dplyr)filter(mtcars, cyl == 8)#> Error: [conflicted] `filter` found in 2 packages.#> Either pick the one you want with `::`#> * dplyr::filter#> * stats::filter#> Or declare a preference with `conflicted_prefer()`#> * conflict_prefer("filter", "dplyr")#> * conflict_prefer("filter", "stats")
As suggested, you can either namespace individual calls:
dplyr::filter(mtcars, am & cyl == 8)#> mpg cyl disp hp drat wt qsec vs am gear carb#> 1 15.8 8 351 264 4.22 3.17 14.5 0 1 5 4#> 2 15.0 8 301 335 3.54 3.57 14.6 0 1 5 8
Or declare a session-wide preference:
conflict_prefer("filter", "dplyr")#> [conflicted] Will prefer dplyr::filter over any other packagefilter(mtcars, am & cyl == 8)#> mpg cyl disp hp drat wt qsec vs am gear carb#> 1 15.8 8 351 264 4.22 3.17 14.5 0 1 5 4#> 2 15.0 8 301 335 3.54 3.57 14.6 0 1 5 8
I recommend declaring preferences directly underneath the corresponding library call:
library(dplyr)conflict_prefer("filter", "dplyr")
You can ask conflicted to report any conflicts in the current session:
conflict_scout()#> 6 conflicts:#> * `filter` : [dplyr]#> * `intersect`: [dplyr]#> * `lag` : dplyr, stats#> * `setdiff` : [dplyr]#> * `setequal` : [dplyr]#> * `union` : [dplyr]
Functions surrounded by []
have been chosen using one of the built-in
rules. Here filter()
has been selected because of the preference
declared above; the set operations have been selected because they
follow the superset principle and extend the API of the base
equivalents.
Loading conflicted creates a new “conflicted” environment that is
attached just after the global environment. This environment contains an
active binding for any object that is exported by multiple packages; the
active binding will throw an error message describing how to
disambiguate the name. The conflicted environment also contains bindings
for library()
and require()
that suppress conflict reporting and
update the conflicted environment with any new conflicts.
It is worth comparing conflicted to
modules and
import. Both packages provide
strict alternatives to library()
, giving much finer control over what
functions are added to the search path.
# modules expects you to namespace all package functionsdplyr <- modules::import_package('dplyr')dplyr$filter(mtcars, cyl == 8)# import expects you to explicit load functionsimport::from(dplyr, select, arrange, dplyr_filter = filter)dplyr_filter(mtcars, cyl == 8)
These require more upfront work than conflicted, in return for greater precision and control.
Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.
Internal has_moved()
function no longer fails when it encounters a
call to .Deprecated()
with no arguments (#29).
.conflicts
environment is correctly removed and replaced each time
a new package is loaded (#28).
conflict_scout()
reports all conflicts found with a set of packages.
conflict_prefer()
allows you to declare a persistent preference
(within a session) for one function over another (#4).
conflicts now generally expects packages that override functions in base
packages to obey the "superset principle", i.e. that foo::bar(...)
must
return the same value of base::bar(...)
whenever the input is not an
error. In other words, if you override a base function you can only extend
the API, not change or reduce it.
There are two exceptions. If the arguments of the two functions are not
compatible (i.e. the function in the package doesn't include all
arguments of the base package), conflicts can tell it doesn't follow
the superset principle. Additionally, dplyr::lag()
fails to follow
the superset principle, and is marked as a special case (#2).
Functions that have moved between packages (i.e. functions with a call to
.Deprecated("pkg::foo")
) as the first element of the function body) will
never generate conflicts.
conflicted now listens for detach()
events and removes conflicts that
are removed by detaching a package (#5)