From Martin Maechler <maechler@stat.math.ethz.ch>  Fri Aug 15 16:06:35 1997
From: Martin Maechler <maechler@stat.math.ethz.ch> (Martin Maechler)
Date: Fri, 15 Aug 1997 17:06:35 +0200
Subject: R-alpha: (minor?) S-R inconsistency:  NULL =~= list()  -- useful is.ALL function
Message-ID: <199708151506.RAA02837@sophie.ethz.ch>


In S, 
	NULL
and
	list()
are not the same.

In R they are (I think).

---------------------------------------------------

At least,

	is.list(NULL) #-> 'F' in S;   'TRUE' in R


Yes: I had an instance where this broke correct S code:

	match(c("xlab","ylab"), names(list(...)))

when '...' is empty, 
gives an error in R,
but gives
	c(NA,NA)
in S.

------------
You may like my function 'is.ALL(.)' for detecting things like these :
(actually some more functions;
 a relatively nice example of using NextMethod(..) for a new "print" method
)

is.ALL <- function(obj, func.names = ls("library:base"),
                   not.using = c("is.single", "is.na.data.frame", "is.loaded"),
                   true.only = FALSE)
{
  ## Purpose: show many 'attributes' of  R object __obj__
  ## -------------------------------------------------------------------------
  ## Arguments: obj: any R object
  ## -------------------------------------------------------------------------
  ## Author: Martin Maechler, Date:  6 Dec 96, 15:23

  is.fn <- func.names[substring(func.names,1,3) == "is."]
  use.fn <- is.fn[ is.na(match(is.fn, not.using))]

  r <- if(true.only) character(0)
  else structure(vector("list", length= length(use.fn)), names= use.fn)
  for(f in use.fn) {
    if(any(f == c("is.na", "is.finite"))) {
      if(!is.list(obj) && !is.vector(obj) && !is.array(obj)) {
        if(!true.only) r[[f]] <- NA
        next
      }
    }
    ##prt.DEBUG("f =",f,"; last rr:", if(f!=use.fn[1]) rr else ".. not yet..")
    rr <-  (get(f))(obj)
    if(!is.logical(rr)) cat("f=",f," --- rr  is NOT logical  = ",rr,"\n")
    ##if(1!=length(rr))   cat("f=",f," --- rr NOT of length 1; = ",rr,"\n")
    if(true.only && length(rr)==1 && rr) r <- c(r, f)
    else if(!true.only) r[[f]] <- rr
  }
  if(is.list(r)) structure(r, class = "isList") else r
}

print.isList <- function(r, ...)
{
  ## Purpose:  print METHOD  for  'isList' objects
  ## -------------------------------------------------------------------------
  ## Arguments:
  ## -------------------------------------------------------------------------
  ## Author: Martin Maechler, Date: 12 Mar 97, 15:07
  ## >>>>> needs  cmp.logical  --> /u/maechler/R/Util.R
  if(is.list(r)) {
    nm <- format(names(r))
    rr <- lapply(r,cmp.logical)
    for(i in seq(along=r)) cat(nm[i],":",rr[[i]],"\n", ...)
  } else NextMethod("print", ...)
}

cmp.logical <- function(log.v)
{
  ## Purpose: compact printing of logicals
  ## -------------------------------------------------------------------------
  ## Arguments: log.v : logical vector
  ## -------------------------------------------------------------------------
  ## Author: Martin Maechler, Date: 13 Dec 96, 16:28
  if(!is.logical(log.v)) {
    warning("coercing argument 'log.v' to logical")
    log.v <- as.logical(log.v)
  }
  structure(if(length(log.v) == 0) "()" else c(".","|")[ 1+ log.v],
	    class = "noquote")
}


## The constructor function
noquote <- function(obj) {
  if(!inherits(obj,"noquote"))
    class(obj) <- c(class(obj),"noquote")
  obj
}
##-- this is just like 'expression'
"[.noquote" <- function (x, subs) structure(unclass(x)[subs], class = "noquote")
## A method for (character) objects of class  'noquote' :
print.noquote <- function(obj,...) {
  cl <- class(obj)
  class(obj) <- cl[cl != "noquote"]
  NextMethod('print', obj, quote = F, ...)
}


### ------------------------------------------------------------------
### Here are some examples on its (is.ALL)  usage :


is.ALL(NULL)

###------- more compactly : ------------ here, we see that   NULL "==" list()

is.ALL(NULL,   true.only = TRUE)
## [1] "is.atomic" "is.list"   "is.null"  
is.ALL(list(), true.only = TRUE)
## [1] "is.atomic" "is.list"   "is.null"  



is.ALL(1:5)
is.ALL(array(1:24, 2:4))
is.ALL(1 + 3)
e13 <- expression(1 + 3)
is.ALL(e13)
## fails (0.50-a) [is.loaded]
##   is.ALL(e13, not.using=c("is.single", "is.finite", "is.na"))
is.ALL(y ~ x) #--> (0.49):  NA  for 'is.na' (& is.finite)



is.ALL(numeric(0), true=T)
is.ALL(array(1,1:3), true=T)
is.ALL(cbind(1:3), true=T)
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
r-devel mailing list -- Read http://www.ci.tuwien.ac.at/~hornik/R/R-FAQ.html
Send "info", "help", or "[un]subscribe"
(in the "body", not the subject !)  To: r-devel-request@stat.math.ethz.ch
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-


R-alpha: (minor?) S-R inconsistency: NULL =~= list() -- useful is.ALL function
Martin Maechler