Skip to content

Conversation

DavisVaughan
Copy link
Member

@DavisVaughan DavisVaughan commented Oct 3, 2025

Closes #198 - Dark hackery no longer needed
Closes #199 - signalCondition() no longer needed
Closes #162 - No longer need this if signal_stage() is gone
Closes #91 - No longer need this if signal_stage() is gone
Closes #176 - It's very fast now
Closes #158 - No longer needed if we have no way to report on superseded functions.

This PR soft-deprecates signal_stage() and removes usage of it from deprecate_soft(), deprecate_warn(), and deprecate_stop(), so they are now very fast after all my other work here.

It also officially soft-deprecates signal_experimental() and signal_superseded(), so they can eventually be removed alongside signal_stage().


TLDR: In the tidyverse we should really encourage including a fixed id in our deprecate_soft() and deprecate_warn() calls. It makes them much much faster.

If you're using id and aren't using always = TRUE, it is VERY fast now. This is what dplyr and friends can start using everywhere for all deprecate_soft() and deprecate_warn() calls.

cross::bench_versions(
  pkgs = c(
    "lifecycle",
    "r-lib/lifecycle",
    "r-lib/lifecycle#202"
  ),
  \() {
    library(lifecycle)

    # trigger the per session warning once
    deprecate_soft("1.1.0", I("my thing"), details = "because", id = "foo")

    bench::mark(
      deprecate_soft("1.1.0", I("my thing"), details = "because", id = "foo"),
      iterations = 100000
    )
  }
)
#> # A tibble: 3 × 7
#>   pkg                 expression                               min   median `itr/sec` mem_alloc `gc/sec`
#>   <chr>               <bch:expr>                          <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 lifecycle           "deprecate_soft(\"1.1.0\", I(\"my … 145.22µs 154.53µs     6339.    8.55KB     44.2
#> 2 r-lib/lifecycle     "deprecate_soft(\"1.1.0\", I(\"my …  27.27µs  28.76µs    33428.    8.56KB     70.3
#> 3 r-lib/lifecycle#202 "deprecate_soft(\"1.1.0\", I(\"my …   5.54µs   5.86µs   160663.    8.56KB    183.

If you aren't using id, then we have to recreate your message every time to have a unique key, so it's a bit slower, but still quite a bit faster than where we were!

Here's an example with AsIs messages that don't require any what expression parsing

cross::bench_versions(
  pkgs = c(
    "lifecycle",
    "r-lib/lifecycle",
    "r-lib/lifecycle#202"
  ),
  \() {
    library(lifecycle)

    # trigger the per session warning once
    deprecate_soft("1.1.0", I("my thing"), details = "because")

    bench::mark(
      deprecate_soft("1.1.0", I("my thing"), details = "because"),
      iterations = 100000
    )
  }
)
#> # A tibble: 3 × 7
#>   pkg                 expression                                min  median `itr/sec` mem_alloc `gc/sec`
#>   <chr>               <bch:expr>                            <bch:t> <bch:t>     <dbl> <bch:byt>    <dbl>
#> 1 lifecycle           "deprecate_soft(\"1.1.0\", I(\"my th… 225.1µs   237µs     4138.    8.55KB     43.0
#> 2 r-lib/lifecycle     "deprecate_soft(\"1.1.0\", I(\"my th…  38.6µs  40.1µs    24288.    8.56KB     61.8
#> 3 r-lib/lifecycle#202 "deprecate_soft(\"1.1.0\", I(\"my th…  17.5µs  18.4µs    52918.    8.56KB     86.9

If you are using the expression parsing features of lifecycle and you aren't using a static id, then it's still not super fast. But remember this problem goes away entirely if you use a static id!

cross::bench_versions(
  pkgs = c(
    "lifecycle",
    "r-lib/lifecycle",
    "r-lib/lifecycle#202"
  ),
  \() {
    library(lifecycle)

    # trigger the per session warning once
    deprecate_soft("1.1.0", "my_fn(arg =)", "my_fn()")

    bench::mark(
      deprecate_soft("1.1.0", "my_fn(arg =)", "my_fn()"),
      iterations = 100000
    )
  }
)
#> # A tibble: 3 × 7
#>   pkg                 expression                                min  median `itr/sec` mem_alloc `gc/sec`
#>   <chr>               <bch:expr>                            <bch:t> <bch:t>     <dbl> <bch:byt>    <dbl>
#> 1 lifecycle           "deprecate_soft(\"1.1.0\", \"my_fn(a… 344.8µs 363.5µs     2716.    8.55KB     41.8
#> 2 r-lib/lifecycle     "deprecate_soft(\"1.1.0\", \"my_fn(a…  92.2µs  96.3µs    10125.    8.56KB     46.5
#> 3 r-lib/lifecycle#202 "deprecate_soft(\"1.1.0\", \"my_fn(a…    54µs  55.8µs    17425.    8.56KB     53.1

And remove all usage of it internally
expect_warning(eval_bare(call2(fn), env(global_env())), "was deprecated")
})

test_that("deprecation warnings are not displayed again", {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test was super broken and wasn't testing what we thought it was. Mostly due to the deprecate_warn() calls needing to be wrapped in an actual function to simulate real usage and make them warn the right way.

@DavisVaughan DavisVaughan requested review from hadley and lionel- October 3, 2025 20:49
Copy link
Member

@hadley hadley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sense to me.

Copy link
Member

@lionel- lionel- left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't supply a what but still supply an id it's still very fast right?

Relatedly I think it's worth adding an annotation or comment of some kind to deprecate_warn0() that msg is passed lazily. The default assumption for R code should be that the timing of evaluation doesn't matter, so going against that should be declared/documented.

@@ -1,29 +1,45 @@
#' Signal other experimental or superseded features
#' Deprecated functions for signalling lifecycle stages
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I'm not sure it's worth deprecating these functions.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Decided over Zed to go ahead and soft-deprecate them with the goal of having an easy removal in a few years

DavisVaughan and others added 2 commits October 7, 2025 08:15
Co-authored-by: Lionel Henry <lionel.hry@proton.me>
@DavisVaughan DavisVaughan merged commit 24710ef into main Oct 7, 2025
14 checks passed
@DavisVaughan DavisVaughan deleted the feature/no-signal-stage branch October 7, 2025 12:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants