En realidad, esto es un poco más complicado de lo que parece. Dado que una lista en realidad (con algo de esfuerzo) puede contener elementos NULL, puede que no sea suficiente para verificar is.null(foo$a). Una prueba más estricta podría ser verificar que el nombre esté realmente definido en la lista:
foo <- list(a=42, b=NULL)
foo
is.null(foo[["a"]]) # FALSE
is.null(foo[["b"]]) # TRUE, but the element "exists"...
is.null(foo[["c"]]) # TRUE
"a" %in% names(foo) # TRUE
"b" %in% names(foo) # TRUE
"c" %in% names(foo) # FALSE
... y foo[["a"]]es más seguro que foo$a, ya que este último usa una coincidencia parcial y, por lo tanto, también puede coincidir con un nombre más largo:
x <- list(abc=4)
x$a # 4, since it partially matches abc
x[["a"]] # NULL, no match
[ACTUALIZAR] Entonces, volvamos a la pregunta de por qué exists('foo$a')no funciona. La existsfunción solo comprueba si existe una variable en un entorno, no si existen partes de un objeto. La cadena "foo$a"se interpreta literariamente: ¿Existe una variable llamada "foo $ a"? ... y la respuesta es FALSE...
foo <- list(a=42, b=NULL) # variable "foo" with element "a"
"bar$a" <- 42 # A variable actually called "bar$a"...
ls() # will include "foo" and "bar$a"
exists("foo$a") # FALSE
exists("bar$a") # TRUE
!is.null(foo$a)(o!is.null(foo[["a"]])para estar seguro)? (oexists("a",where=foo))