Common type of two causal weight vectors of the same class
Source:R/causal-weights-coercion.R
causal_wts_ptype2.Rdcausal_wts_ptype2() supplies the one coercion rule that concrete weight
classes share: two causal weight vectors combine only when their estimands
are identical, and otherwise the common type is a plain double. Concrete
classes call it from inside their own vctrs::vec_ptype2() method for the
pairing of two vectors of that class.
Arguments
- x, y
The two weight vectors being combined, as passed to
vec_ptype2().- ptype
The common type to return when the estimands agree. Returned exactly as supplied and never inspected, and evaluated only on that path.
- on_downgrade
A function of no arguments, called once when the estimands differ. Its value is discarded; it exists so the caller can signal a condition of its own choosing.
Value
ptype when estimand(x) and estimand(y) are identical, and
double() otherwise.
Details
This is a helper rather than a method, and it cannot become one. vctrs
resolves vec_ptype2() with an exact lookup on class(x)[[1]] and never
walks the rest of the class vector, so a method registered on causal_wts is
never found for a concrete subclass. Each package therefore keeps its own
vec_ptype2.<cls>.<cls> method and calls this from inside it:
vec_ptype2.bw.bw <- function(x, y, ...) {
causal_wts_ptype2(
x,
y,
new_bw(estimand = estimand(x)),
on_downgrade = function() warn_bw_downgrade("bw")
)
}The estimands are read through estimand(), so a class that computes its
estimand rather than storing it is compared on the value its method returns.
Two vectors that record no estimand are compatible, which is the ordinary
case for continuous-exposure weights rather than an edge case.
ptype is evaluated only on the compatible path. A caller's prototype is
usually a live constructor call over metadata the two vectors do not agree
on, so the downgrade path never forces it.
The condition belongs to the caller. This function signals nothing of its
own: on_downgrade() runs exactly once when the estimands differ, and
whatever it signals reaches the user unchanged. The concrete classes
downstream disagree on the class of that condition and on whether the
downgrade warns at all, so choosing one here would break them.
The scope is one pairing and one rule, which is as much as the concrete
classes share. A class that checks nothing beyond the estimand hands over its
whole method, as balancing does. A class that checks further metadata can
use this for the estimand alone and keep its own control flow: propensity
compares five more fields after the estimand, each with its own message and
its own bail-out. Such a caller can pass a sentinel as ptype and carry on
with its remaining checks when that sentinel comes back.
See also
new_causal_wts() for the class this rule is written against, and
estimand() for the accessor it compares.
Examples
ate <- new_causal_wts(c(1.2, 0.8), subclass = "my_wts", estimand = "ate")
att <- new_causal_wts(c(1.0, 1.5), subclass = "my_wts", estimand = "att")
# Matching estimands give back the prototype the caller built.
causal_wts_ptype2(
ate,
ate,
new_causal_wts(subclass = "my_wts", estimand = estimand(ate)),
on_downgrade = function() message("the estimands differ")
)
#> <my_wts[0]>
# Differing estimands run the callback and drop to a bare double.
causal_wts_ptype2(
ate,
att,
new_causal_wts(subclass = "my_wts", estimand = estimand(ate)),
on_downgrade = function() message("the estimands differ")
)
#> the estimands differ
#> numeric(0)