Tim Taylor
2025-Oct-22 23:04 UTC
[Rd] Is structure(NA, class = c("def", "condition")) a valid 'condition' object?
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 at 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 at 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 at r-project.org mailing list >>> https://stat.ethz.ch/mailman/listinfo/r-devel >> > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel
Lionel Henry
2025-Oct-24 16:18 UTC
[Rd] Is structure(NA, class = c("def", "condition")) a valid 'condition' object?
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 at 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 at
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
at 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 at r-project.org mailing list
> >>> https://stat.ethz.ch/mailman/listinfo/r-devel
> >>
> >
> > ______________________________________________
> > R-devel at r-project.org mailing list
> > https://stat.ethz.ch/mailman/listinfo/r-devel
>
> ______________________________________________
> R-devel at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel