https://github.com/bioconductor/uniquegeneric

A minimalist R package that only defines a unique() S4 generic.

https://github.com/bioconductor/uniquegeneric

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
Last synced: 10 months ago · JSON representation

Repository

A minimalist R package that only defines a unique() S4 generic.

Basic Info
  • Host: GitHub
  • Owner: Bioconductor
  • Language: R
  • Default Branch: devel
  • Homepage:
  • Size: 3.91 KB
Statistics
  • Stars: 0
  • Watchers: 9
  • Forks: 0
  • Open Issues: 0
  • Releases: 0
Created about 8 years ago · Last pushed over 3 years ago
Metadata Files
Readme

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

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
Top Authors
Issue Authors
Pull Request Authors
Top Labels
Issue Labels
Pull Request Labels