0

In R, I can prefix functions with the name of the package they belong to (e.g., dplyr::select). Nevertheless, I am having problems when doing this with c from the terra package. I can do it fine with base::c (should I want to):

base::c(1, 2, 3)
# [1] 1 2 3   

However, I run into problems when running similar code for terra:

# Dummy SpatRaster
foo <- terra::rast(matrix(1:9, ncol=3))

# Works fine
c(foo, foo) 

# Not so much
terra::c(foo, foo)
# Error: 'c' is not an exported object from 'namespace:terra'

I am confused how c is not an exported function of terra and yet I can access and use it just fine... so long as I don't use a prefix.

Q: Can someone explain why this is the case and how I can explicitly refer to c from terra?


PS ?terra::c gives a help page explaining how c combines SpatRasterobjects into a new SpatRaster object, which suggests to me that this function must've been implemented in the terra package.

Dan
  • 11,370
  • 4
  • 43
  • 68
  • Have you tried `terra:::c` (unexported)? It is not in the package [`NAMESPACE`](https://github.com/rspatial/terra/blob/master/NAMESPACE) file, suggesting that it is relying on the S3/S4 mechanics. (I don't have `terra` installed or I would test it myself.) (It appears to be an S4 method, btw: https://github.com/rspatial/terra/blob/8340655889e65757ce9cffbe3ec04afd043d7bd2/R/SpatDataSet.R#L76) – r2evans Dec 03 '20 at 14:49
  • @r2evans that gives an error, also, but a new error: `Error in get(name, envir = asNamespace(pkg), inherits = FALSE) : object 'c' not found`. – Dan Dec 03 '20 at 14:54
  • 1
    Yeah, that suggests (to me) that it's an S4 (not S3) method, consistent with not finding `c.Spat*` in the code (though I didn't search all files). Does `showMethods("c")` suggest its presence? If so, `getMethod("c", "SpatRaster")` (or whatever its signature is) should return the function, then you can either assign that to a "regular" function or (if rare) use it immediately with `getMethod(...)(1,2,3)`. – r2evans Dec 03 '20 at 15:00
  • 2
    You don't need to irectly refer to the `c` function of `terra`. How I understand it from the documentation, the `c` function is "S4 method for signature 'SpatRaster' ", meaning that it is not a new function from terra, it only extends the existing `base::c` function by adding a new signature. You can call `base::c(foo, foo)` and it works fine. – Jonas Dec 03 '20 at 15:00
  • 1
    `showMethods("c")` show `x="SpatRaster"` and `x="SpatDataSet"`. Thanks, Jonas – I guess I didn't really realise that it was an extension to `base::c`, but that makes sense given the behaviour. Clearly, I need to read up on how such extensions work. Thanks to you both for your help! – Dan Dec 03 '20 at 15:09

1 Answers1

2

This is because c is a "primitive" function --- these follow their own rules. A package cannot / need not import and export them

c
#function (...)  .Primitive("c")

This is not the case, for, for example, nrow

nrow
#function (x) 
#dim(x)[1L]
#<bytecode: 0x000000001662d228>
#<environment: namespace:base>

And terra creates a generic function from it, and exports it so that you can do terra::nrow().

Your question triggered me to look a bit more at this, and I noted that in the current version of terra many primitive functions do not work if you do not load the package with library(terra). For example, you get

foo <- terra::rast(matrix(1:9, ncol=3))
x <- c(foo, foo) 
max(x)
#Error in x@ptr$summary(fun, na.rm, .terra_environment$options@ptr) : 
#  trying to get slot "ptr" from an object of a basic class ("NULL") with no slots 

I just fixed that in the development version; and with that version the above returns

#class       : SpatRaster 
#dimensions  : 3, 3, 1  (nrow, ncol, nlyr)
#resolution  : 1, 1  (x, y)
#extent      : 0, 3, 0, 3  (xmin, xmax, ymin, ymax)
#coord. ref. : +proj=longlat +datum=WGS84 +no_defs 
#source      : memory 
#names       : max 
#min values  :   1 
#max values  :   9 
Robert Hijmans
  • 40,301
  • 4
  • 55
  • 63