> CAN CONDITIONS BE ENVIRONMENTS FWIW, in reticulate for a time we converted Python Exceptions to ENVSXP R objects, but ran into quite a few issues. We switched over to presenting Python Exceptions as R lists with an attribute to the external pointer.
A close reading of the R API description would suggest that R conditions objects can be any object type so long as the appropriate accessor methods are defined, but practically speaking, I think too much code out there today assumes that all conditions are lists, and it wasn't worth the effort swimming against the current on this. Some discussion from that time: https://github.com/r-lib/rlang/issues/1664 On Fri, Oct 24, 2025 at 12:19 PM Lionel Henry via R-devel <[email protected]> wrote: > > Hello, > > I wanted to share some thoughts based on our experience dealing with > conditions > in rlang and Positron, which might be relevant to the discussion about > condition > storage types. > > For context, rlang provides infrastructure for chainable conditions that carry > backtrace information. It provides `entrace()` which can be used as (global) > calling handler to promote all errors to these augmented errors, and this is > used by default in Positron as our mechanism to record backtraces in the > console > (in a way that is more persistent than `traceback()`). > > First it's worth noting that base R already uses lists for conditions. For > example, `errorCondition()`, `warningCondition()`, and `simpleCondition()` all > construct list-based conditions: > > ```r > typeof(errorCondition("foo")) > #> [1] "list" > > typeof(warningCondition("foo")) > #> [1] "list" > > typeof(simpleCondition("foo")) > #> [1] "list" > ``` > > And the `R_makeErrorCondition` implementation in C code also creates > list-based > conditions. So there's already a strong precedent in base R for conditions > being > lists, and for that reason rlang only produces list-storage condition. > > Building on this assumption, rlang documents list fields like `trace` and > `parent` that are accessed via `[[`. You can implement support for these > fields > in any kind of conditions, not just rlang ones. This has worked well for us: > > - No need to depend on generics or constructors from other packages > - No performance cost on inspection > - Easier to access values from C code if needed > - Simpler for debugging tools to inspect conditions > > It's worth noting that rlang has been assuming list storage (or at least `[[` > support) in its global handler for a long time without issue. Positron uses > this > handler by default. > > We still find methods like `conditionMessage()` extremely useful. With chained > errors and complex formatting, building the message upfront can be wasteful > (if > the error is handled the message is not printed). On the other hand, > `conditionCall()` hasn't been as useful in practice, and a simple "call" field > stored in the list would have sufficed. > > So we'd suggest: > > - Conditions are lists of fields > - Accessing `message` and `call` by name instead of method calls remains > undefined behavior (especially for `message` which benefits from laziness) > > This makes the type of conditions clearer and simpler to examine for users and > development tools during debugging sessions. It also makes it easier to > develop > conventions based on the presence and type of fields like `trace` and > `parent`. > > Best, > Lionel > > On Thu, Oct 23, 2025 at 1:04 AM Tim Taylor > <[email protected]> wrote: > > > > FWIW - I read this the same way as you Henrik. I presume if there was a > > desire to restrict the underlying typeof the condition a dedicated > > constructor would have been supplied and the type enforced. Whether this > > would be better or worse is an interesting thing to ponder. > > > > Tim > > > > > On 8 Oct 2025, at 16:43, Henrik Bengtsson <[email protected]> > > > wrote: > > > > > > Thank you, Duncan. > > > > > > It sounds like you're reading it the same as I, i.e. what > > > typeof(<condition>) should be is not specified. > > > > > > Using list() in my tiny example was a thinko - NULL would indeed have > > > been better. > > > > > > /Henrik > > > > > >> On Wed, Oct 8, 2025 at 5:32 AM Duncan Murdoch <[email protected]> > > >> wrote: > > >> > > >> Besides `conditionMessage` and `conditionCall`, base R also has methods > > >> defined for `as.character` and `print`, but they appear to make no > > >> assumptions about the object other than having `conditionMessage` and > > >> `conditionCall` defined. > > >> > > >> The help page is silent about what type of thing `conditionCall()` > > >> should return, but the objects produced by the standard condition > > >> functions will return the `call` argument, which defaults to `NULL`, but > > >> could be a "call expression". > > >> > > >> So I'm not sure your definition of the `conditionCall()` methods is > > >> going to work: `list()` doesn't return an expression. Returning > > >> `NULL` would be better. > > >> > > >> Of course, in S3 "valid" isn't defined formally; it just means something > > >> that won't mess up. So it's quite possible `list()` is okay. > > >> > > >> Duncan Murdoch > > >> > > >> > > >>> On 2025-10-07 7:42 p.m., Henrik Bengtsson wrote: > > >>> I think structure(NA, class = c("def", "condition")) is a valid > > >>> 'condition' object. Am I wrong? > > >>> > > >>> BACKGROUND: > > >>> > > >>> The abstract 'condition' class: why type or mode can a 'condition' > > >>> object have? > > >>> > > >>> In help("condition"), we can read that: > > >>> > > >>> "Conditions are objects inheriting from the abstract class condition. > > >>> ..." > > >>> > > >>> and then it specifies the API, i.e. the methods it should support, e.g. > > >>> > > >>> "The functions conditionMessage and conditionCall are generic > > >>> functions that return the message and call of a condition." > > >>> > > >>> Then we have several functions for creating 'condition' objects, e.g. > > >>> > > >>>> simpleCondition > > >>> function (message, call = NULL) > > >>> { > > >>> class <- c("simpleCondition", "condition") > > >>> structure(list(message = as.character(message), call = call), > > >>> class = class) > > >>> } > > >>> > > >>> AFAIK, all of them create 'condition' object of type 'list'. > > >>> > > >>> > > >>> CAN CONDITIONS BE ENVIRONMENTS OR ATOMIC OBJECTS? > > >>> > > >>> However, is the list type a requirement? I cannot find it specified > > >>> anywhere. The way I interpret help("condition") and how it is > > >>> carefully written using terms like "abstract class" and not mentioning > > >>> the type anywhere, I take it as: > > >>> > > >>> cnd1 <- structure(new.env(), class = c("abc", "condition")) > > >>> > > >>> and > > >>> > > >>> cnd2 <- structure(NA, class = c("def", "condition")) > > >>> > > >>> are both valid 'condition' objects, as long as we define the S3 > > >>> methods for `conditionMessage()` and `conditionCall()`, e.g. > > >>> > > >>> conditionMessage.abc <- function(c) "boom" > > >>> conditionCall.abc <- function(c) list() > > >>> > > >>> conditionMessage.def <- function(c) "boom" > > >>> conditionCall.def <- function(c) list() > > >>> > > >>> FWIW, I create 'condition' objects of type NA in my 'R.oo' package > > >>> going back ~25 years. > > >>> > > >>> Thanks, > > >>> > > >>> Henrik > > >>> > > >>> ______________________________________________ > > >>> [email protected] mailing list > > >>> https://stat.ethz.ch/mailman/listinfo/r-devel > > >> > > > > > > ______________________________________________ > > > [email protected] mailing list > > > https://stat.ethz.ch/mailman/listinfo/r-devel > > > > ______________________________________________ > > [email protected] mailing list > > https://stat.ethz.ch/mailman/listinfo/r-devel > > ______________________________________________ > [email protected] mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel ______________________________________________ [email protected] mailing list https://stat.ethz.ch/mailman/listinfo/r-devel
