I often want to parameterize error messages, e.g.
stop(sprintf("function parameter foo = %s, bar = %s", foo, bar))
This works just fine until something like is.null(foo)
arises. Now, the sprintf
output itself is NULL
, and the error condition message becomes pretty worthless. Likewise, perhaps some piece of code was expecting foo
to be a scalar, but instead it happens to be a length > 1 vector ... sprintf
(and glue
) return a vector of strings, which stop()
happily accepts and then spits that vector back out. One could write a separate error message for all the possible violations that led one to call stop
in the first place, but that also litters the code when usually they're all just basic variants of the same message: "something about foo
and/or bar
is no good."
Before I set down the path of writing a sane error message replacement/simplification of sprintf
, do others here use some existing (hopefully lightweight) tooling to make creating safe error messages easier? Basically a sprintf
, glue
, or paste
/paste0
replacement, but with sane defaults that make sense for error messages 99% of the time. Examples of what I'd consider sane defaults:
- replace
NULL
values with "NULL" (handleable withcapture.output
) - replace other length-0 elements with their printed output, e.g.
character(0)
with "character(0)" (also handleable withcapture.output
) - replace vectors with some truncated/limited view, e.g.
letters[1:10]
with "10 elements (a, b, c, ..., j)" - replace more complicated objects with perhaps just their class name(s) (and perhaps depth-1
str()
output).
I know rlang
has some class-based approaches for this, which is great for when one needs to design their own classes, but for relatively simple error messages handling mostly base data structures (lists & data frames, atomic vectors, environments, etc.), that approach seems heavyweight, and really I just want a saner string interpolation function(s).