Futurize - Tearing Down Parallelization Barriers
in R with Transpilers - Easy!



Henrik Bengtsson

University of California, San Francisco

R Foundation, R Consortium, Bioconductor

@HenrikBengtsson



useR! 2026, Warsaw, Poland (July 8, 2026)

Growing datasets make sequential analysis a bottleneck

  • Bootstrapping confidence intervals - 1,000 replicates × large cohort = hours
  • Mixed models across patient subgroups - dozens of subgroup fits, one after another
  • Simulation studies for sample-size planning - 10,000 iterations on a laptop overnight





32× CPU cores sitting idle

Parallelization can help →

Barrier is friction!

Parallelizing used to require abandoning existing code

Before: sequential, readable, auditable

res <- lapply(patients, fit_model)

💡 Futureverse design goal
- I want your mind and focus to stay here!

After: parallel, hard to read, hard to maintain

library(parallel)

if (parallel) {
  if (.Platform$OS.type == "windows") {
    cl <- makeCluster(8)
    res <- parLapply(cl, patients, fit_model)
    stopCluster(cl)
  } else {
    options(mc.cores = 8)
    res <- mclapply(patients, fit_model)
  }
} else {
  res <- lapply(patients, fit_model)
}
⚠️ “Too distractive and error prone”
Refactoring tax
  • Many parallelization APIs
  • Complicated to test
  • Expensive to maintain

Futureverse was introduced to lower refactoring tax

Before: sequential, readable, auditable

After: parallel, readable, maintainable


res <- lapply(patients, fit_model)
library(future.apply)
plan(multisession)
res <- future_lapply(patients, fit_model)
library(purrr)

res <- map(patients, fit_model)
library(furrr)
plan(multisession)
res <- future_map(patients, fit_model)
library(foreach)

res <- foreach(p = patients) %do% {
  fit_model(p) 
}
library(doFuture)
plan(multisession)
res <- foreach(p = patients) %dofuture% {
  fit_model(p) 
}

The R community has embraced the Futureverse



+35% reverse dependencies yearly
(500+ direct, 1600+ recursive)


Top 0.6% most downloaded packages

… but, we can simplify it further with ‘futurize’






All you need to remember is futurize()

New package futurize preserves your original code

  • Universal adapter: One unifying function futurize()
  • Zero rewrites: Original logic unchanged
res <- lapply(patients, fit_model)
res <- lapply(patients, fit_model) |>
         futurize()
library(purrr)
res <- map(patients, fit_model) |>
         futurize()
library(foreach)
res <- foreach(p = patients) %do% {
  fit_model(p)
} |> futurize()
library(plyr)
res <- llply(patients, fit_model) |>
         futurize()
library(BiocParallel)
res <- bplapply(patients, fit_model) |>
         futurize()

Easy!

Under the hood: transpilation at work

Source-to-source code translation

  • futurize() inspects the expression on the left
  • Dynamically rewrites the call to its parallel equivalent
  • Executes it within Futureverse

Eliminates the refactoring tax

  • No manual rewrites: Keep your code clean and readable
  • No package lock-in: Seamlessly adapts to your preferred package API (base, purrr, foreach, etc.)

Automatic code rewriting

# What you write:
res <- lapply(xs, fit_model) |> futurize()
# What is executed under the hood:
-lapply(xs, fit_model)
+future.apply::future_lapply(xs, fit_model)


# What you write:
res <- map(xs, fit_model) |> futurize()
# What is executed under the hood:
-map(xs, fit_model)
+furrr::future_map(xs, fit_model)

Same code scales from laptop to cloud to HPC

Without changing any code, you can switch from local and remote parallel processing to large-scale high-performance compute (HPC) processing:

plan() Environment Use case
sequential Single machine Sequential (default, debugging)
multisession Single machine Parallel across multiple cores
mirai_multisession Single machine Same as above; powered by mirai
cluster Many machines Parallel across many machines (desktops, cloud)
batchtools_* Slurm/SGE/LSF Scheduler-based HPC clusters
library(futurize)
plan(future.batchtools::batchtools_slurm)
res <- patients |> purrr::map(fit_model) |> futurize()

… we can do even more with ‘futurize’






All you need to remember is futurize()

futurize() works with a growing set of domain-specific packages

CRAN Package Use
boot Bootstrap resampling
caret Machine learning training
DiceKriging Kriging models
ez Easy analysis of variance
fwb Fractional weighted bootstrap
gamlss Generalized additive models
glmmTMB Generalized linear mixed models
glmnet Lasso & elastic-net
kernelshap Kernel SHAP explanations
lme4 Linear mixed-effects models
metafor Meta-analysis models
mgcv Generalized additive models
modelsummary Model summaries & tables
CRAN Package Use
parameters Model parameters
partykit Recursive partitioning
pls Partial least squares
pvclust Hierarchical clustering
riskRegression Risk regression (survival)
sandwich Robust covariance estimators
seriation Data ordering
stars Spatiotemporal data cubes
strucchange Structural change tests
SuperLearner Super Learner ensembles
tm Text mining
vegan Community ecology
Bioc Package Use
BiocParallel Map-reduce infrastructure
DESeq2 Differential gene expression
GenomicAlignments Genomic alignments (BAM)
GSVA Gene set variation analysis
Rsamtools Rsamtools utilities
scater Single-cell transformations
scuttle Single-cell utilities
SingleCellExperiment Single-cell containers
sva Surrogate variable analysis

Bootstrap simulations accelerated with futurize()

Sequential:

library(boot)
b <- boot(data = cohort, statistic = cox_stat, R = 100e3)

100,000 bootstrap replicates takes hours on large cohorts!

A single worker

Parallel:

plan(future.mirai::mirai_multisession)
library(boot)
b <- boot(data = cohort, statistic = cox_stat, R = 100e3) |>
     futurize()

Faster when distributed across parallel workers.
Identical results.

32 parallel workers

Yes, we can do progress reporting too






All you need to remember is progressify()

Progress reporting with ‘progressify’


Vanilla call:

res <- lapply(patients, fit_model)


With progress reporting:

library(progressify)
handlers("cli", globals = TRUE)

res <- lapply(patients, fit_model) |> 
         progressify()
         

■■■■■■■■■■■■■■■■80% | ETA: 12m

In parallel with progress reporting:

library(progressify)
handlers("cli", globals = TRUE)

library(futurize)
plan(multisession)

res <- lapply(patients, fit_model) |> 
         progressify() |> 
         futurize()

■■■■■■■■■■■■■■■■80% | ETA: 23s

Easy!

Coming: Less waste - making better use of compute resources

  • futurize() cancels remaining parallel tasks if there is an error or an interrupt

  • estimate efficiency of parallelization
    Is it worth it? Is there a better parallel backend? (coming)

  • optimize data distribution to parallel workers: (coming)
    shared memory (e.g. new mori package by C. Gao 2026)

  • resource declarations: (coming)

    ys <- lapply(xs, fit) |> 
            resources(memory = 2*GiB, gpu_cores = 2, time = 5*hours) |> 
            futurize()

Go compute and may the future be with you!

Easy to install:

install.packages(c("futurize", "progressify"))

Easy to use:

ys <- lapply(xs, fcn) |> progressify() |> futurize()

Stay with your favorite coding style:

ys <- xs |> map(fcn) |> progressify() |> futurize()

Available elsewhere too:

ys <- glmnet::cv.glmnet(x, y) |> futurize()