0

I want to define an S4 method that return a scalar return value. Here I mean by scalar value , the contrary of a vector.

setGeneric("getScalar", function(value,  ...) 
  standardGeneric("getScalar")
)
setMethod("getScalar", 
          signature(value = "ANY"),
          def = function(value, ...) getScalar(value,...), ## call external function
          valueClass = "atomic" ### atomic is false, what should I do ?
)

I can't override the method by its output , I mean I can't define many function having the same signature with a different return valueClass :numeric , integer , character ,.. So How can I do this?

EDIT to give more context :

I think is atomic is confusing here. I mean by scalar a numeric value or a boolean or a character, of length one. To give more context I will have 3 functions in my package:

dbGetQuery   :return a list/data.frame : i.e some table rows
dbGetScalar  :return a scalar value  : i.e count(*),table_name,..
dbGetNoQuery :return nothing          : update/insert actions

It is an extension to DBI interface.

EDIT2

We can assume that scalar is a vector of length 1. But I can't express this condition using S4. in c# or c, I would write

double[]       // vector
double        // scalar

Maybe I should just change the name of my function.

agstudy
  • 119,832
  • 17
  • 199
  • 261
  • 1
    `?setMethod` says `valueClass` is "Obsolete and unused, ...". I also wasn't aware that "atomic" was the name of a class. Since there is no scalar class I would think the usual approach would be to coerce to "numeric" and return `obj[1]` – IRTFM Nov 07 '13 at 22:44
  • Can you provide an example of what you mean by 'scalar'? I would have said a length 1 vector, but I think you mean something else? `!is.atomic()` so, e.g., a `list()`? Also, working code (maybe a simple function, no methods involved?) without the recursive call would be helpful. – Martin Morgan Nov 07 '13 at 23:00
  • @DWin thanks I was not aware that `valueClass` is obsolete. But when my function return something different than valueClass type it gives me an error. – agstudy Nov 07 '13 at 23:08
  • @MartinMorgan I edit my answer to give more context. – agstudy Nov 07 '13 at 23:09
  • So how is a "scalar" not just a length 1 vector? – IRTFM Nov 07 '13 at 23:15
  • @DWin I said we can assume that scalar is a vector of length 1. – agstudy Nov 07 '13 at 23:20

1 Answers1

2

One possibility is to check the value of the return type after method dispatch

setGeneric("getScalar", function(x, ...) {
    value <- standardGeneric("getScalar")
    if (!is.atomic(value) || length(value) != 1L)
        stop("not a scalar atomic vector")
    value
})

setMethod(getScalar, "ANY", function(x, ...) x)

Another possibility is to define a 'Scalar' class, with a validity check on the base class that enforces the constraint

.Scalar <- setClass("Scalar", contains="ANY", validity=function(object) {
    if (length(object) != 1L)
        "non-scalar object"
    else TRUE
}, prototype=NA)

or controlling scalar types more strongly with a small hierarchy based on a virtual class

setClass("Scalar", validity=function(object) {
    if (length(object) != 1L)
        "non-scalar object"
    else TRUE
})

.ScalarInteger <- setClass("ScalarInteger",
    contains=c("Scalar", "integer"),
    prototype=prototype(NA_integer_))

This is the approach taken in Bioconductor's Biobase package, with a mkScalar constructor.

Martin Morgan
  • 45,935
  • 7
  • 84
  • 112
  • +1! thank you! I think I will choose the first option since it can be used for `dbGetNoQuery` (null return value) with a slight modification. – agstudy Nov 07 '13 at 23:44