https://github.com/bioconductor/uniquegeneric
A minimalist R package that only defines a unique() S4 generic.
Science Score: 13.0%
This score indicates how likely this project is to be science-related based on various indicators:
-
○CITATION.cff file
-
✓codemeta.json file
Found codemeta.json file -
○.zenodo.json file
-
○DOI references
-
○Academic publication links
-
○Academic email domains
-
○Institutional organization owner
-
○JOSS paper metadata
-
○Scientific vocabulary similarity
Low similarity (6.8%) to scientific vocabulary
Repository
A minimalist R package that only defines a unique() S4 generic.
Basic Info
Statistics
- Stars: 0
- Watchers: 9
- Forks: 0
- Open Issues: 0
- Releases: 0
Metadata Files
README.md
A hard-to-reproduce bug in callNextMethod()
uniqueMethod and uniqueGeneric are minimalist packages that can be used to reproduce the bug.
Quick and easy way to reproduce the bug
devtools::install_github("Bioconductor/uniqueMethod")
devtools::install_github("Bioconductor/uniqueGeneric")
library(uniqueMethod)
library(uniqueGeneric)
setClass("B", slots=c(values="integer"))
setMethod("unique", "B",
function(x, incomparables=FALSE, ...) {
x@values <- unique(x@values, incomparables=incomparables, ...)
x
}
)
setClass("C", contains="B")
setMethod("unique", "C",
function(x, incomparables=FALSE, ...) callNextMethod()
)
unique(new("C"))
#Error in unique.default(x = x) : unique() applies only to vectors
Details
The unique() S4 generic defined in uniqueGeneric is defined with
setGeneric("unique", signature="x") so is different from the
base::unique() implicit S4 generic defined by uniqueMethod:
library(uniqueMethod)
library(uniqueGeneric)
uniqueMethod::unique
# standardGeneric for "unique" defined from package "base"
#
# function (x, incomparables = FALSE, ...)
# standardGeneric("unique")
# <environment: 0x43e3898>
# Methods may be defined for arguments: x, incomparables
# Use showMethods("unique") for currently available ones.
showMethods(uniqueMethod::unique)
# Function: unique (package base)
# x="A"
# x="ANY"
uniqueGeneric::unique
# standardGeneric for "unique" defined from package "uniqueGeneric"
#
# function (x, incomparables = FALSE, ...)
# standardGeneric("unique")
# <bytecode: 0x46fa718>
# <environment: 0x46f22a8>
# Methods may be defined for arguments: x
# Use showMethods("unique") for currently available ones.
showMethods(uniqueGeneric::unique)
# Function: unique (package uniqueGeneric)
# x="ANY"
Since uniqueGeneric::unique() masks uniqueMethod::unique(), the
following is expected:
unique(new("A"))
# Error in unique.default(new("A")) : unique() applies only to vectors
uniqueMethod:::unique(new("A"))
# Hi, I'm the uniqueMethod::unique() method for A objects.
Now let's define a method on uniqueGeneric::unique. For this we introduce
a new class:
setClass("B", slots=c(values="integer"))
setMethod("unique", "B",
function(x, incomparables=FALSE, ...) {
x@values <- unique(x@values, incomparables=incomparables, ...)
x
}
)
Note that something is already going wrong with selectMethod():
showMethods(uniqueGeneric::unique)
# Function: unique (package uniqueGeneric)
# x="ANY"
# x="B"
selectMethod(uniqueGeneric::unique, "B") # finds the method
showMethods(unique)
# Function: unique (package uniqueGeneric)
# x="ANY"
# x="B"
selectMethod(unique, "B") # finds the method
showMethods("unique")
# Function: unique (package uniqueGeneric)
# x="ANY"
# x="B"
selectMethod("unique", "B") # does NOT find the method!
However, despite this, method dispatch works as expected:
b <- new("B", values=c(6:3, 1:4))
unique(b)
# An object of class "B"
# Slot "values":
# [1] 6 5 4 3 1 2
But calls to callNextMethod() that happen from within a method defined
on the uniqueGeneric::unique() generic are broken:
setClass("C", contains="B")
c <- new("C", b)
uniqueGeneric::unique(c)
# An object of class "C"
# Slot "values":
# [1] 6 5 4 3 1 2
setMethod("unique", "C",
function(x, incomparables=FALSE, ...) callNextMethod()
)
showMethods(uniqueMethod::unique)
# Function: unique (package base)
# x="A"
# x="ANY"
showMethods(uniqueGeneric::unique)
# Function: unique (package uniqueGeneric)
# x="ANY"
# x="B"
# x="C"
uniqueGeneric::unique(new("C"))
# Error in unique.default(x = x) : unique() applies only to vectors
Very surprisingly, the uniqueMethod::unique() generic now also seems
to have a method for C objects defined on it:
showMethods(uniqueMethod::unique)
# Function: unique (package base)
# x="A"
# x="ANY"
# x="C"
but selectMethod() can't find it:
selectMethod(uniqueMethod::unique, "C")
# Method Definition (Class "derivedDefaultMethod"):
#
# function (x, incomparables = FALSE, ...)
# UseMethod("unique")
# <bytecode: 0x3c7fe60>
# <environment: namespace:base>
#
# Signatures:
# x
# target "C"
# defined "ANY"
It seems that when we did uniqueGeneric::unique(new("C")), the right
method was was found but callNextMethod() went looking for the next
method in the wrong generic. This can be observed by defining a class
that extends A and defining a uniqueGeneric::unique() method for it:
setClass("D", contains="A")
setMethod(uniqueGeneric::unique, "D",
function(x, incomparables=FALSE, ...) callNextMethod()
)
showMethods(uniqueMethod::unique)
# Function: unique (package base)
# x="A"
# x="ANY"
# x="C"
showMethods(uniqueGeneric::unique)
# Function: unique (package uniqueGeneric)
# x="ANY"
# x="B"
# x="C"
# x="D"
When calling uniqueGeneric::unique(new("D")), the next method in the
method table for uniqueGeneric::unique() is the default method i.e.
the method for signature "ANY". But that's not what's happening:
uniqueGeneric::unique(new("D"))
# Hi, I'm the uniqueMethod::unique() method for A objects.
# NULL
Owner
- Name: Bioconductor
- Login: Bioconductor
- Kind: organization
- Website: https://bioconductor.org
- Repositories: 156
- Profile: https://github.com/Bioconductor
Software for the analysis and comprehension of high-throughput genomic data
GitHub Events
Total
Last Year
Issues and Pull Requests
Last synced: over 1 year ago
All Time
- Total issues: 0
- Total pull requests: 0
- Average time to close issues: N/A
- Average time to close pull requests: N/A
- Total issue authors: 0
- Total pull request authors: 0
- Average comments per issue: 0
- Average comments per pull request: 0
- Merged pull requests: 0
- Bot issues: 0
- Bot pull requests: 0
Past Year
- Issues: 0
- Pull requests: 0
- Average time to close issues: N/A
- Average time to close pull requests: N/A
- Issue authors: 0
- Pull request authors: 0
- Average comments per issue: 0
- Average comments per pull request: 0
- Merged pull requests: 0
- Bot issues: 0
- Bot pull requests: 0