[Rd] Is structure(NA, class = c("def", "condition")) a valid 'condition' object?
Tomasz Kalinowski
k@||now@k|t @end|ng |rom gm@||@com
Fri Oct 24 19:44:03 CEST 2025
> 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
<r-devel using r-project.org> 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
> <tim.taylor using hiddenelephants.co.uk> 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 <henrik.bengtsson using gmail.com> 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 <murdoch.duncan using gmail.com> 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
> > >>>
> > >>> ______________________________________________
> > >>> R-devel using r-project.org mailing list
> > >>> https://stat.ethz.ch/mailman/listinfo/r-devel
> > >>
> > >
> > > ______________________________________________
> > > R-devel using r-project.org mailing list
> > > https://stat.ethz.ch/mailman/listinfo/r-devel
> >
> > ______________________________________________
> > R-devel using r-project.org mailing list
> > https://stat.ethz.ch/mailman/listinfo/r-devel
>
> ______________________________________________
> R-devel using r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
More information about the R-devel
mailing list