data-raw/hrf_todo.md

HRF Refactoring To-Do List

This plan outlines the incremental steps to refactor the R/hrf.R module based on the principles in hrf_refactor.md. The goal is a more modular system using as_hrf, decorators, and a single evaluate.HRF, verified against tests/testthat/test_hrf.R.

Phase 1: Core Constructor and Basis Binding

Phase 2: Implement Decorators

Phase 3: Integrate Decorators and Refactor gen_hrf

Phase 4: Unify Evaluation and Remove Redundancy

Phase 5: Final Integration and Cleanup

Folding the "odds‑and‑ends" into the one‑page HRF core

Below I show how each of the three helpers melts away once we have the canonical constructor as_hrf() and the composable decorator bind_basis().

1 Empirical HRF → 3‑line wrapper

old ------------------------------------------------------

gen_empirical_hrf <- function(t, y, name = "empirical_hrf") { f <- approxfun(t, y, yright = 0, yleft = 0) HRF(f, name = name, nbasis = 1) }

new ------------------------------------------------------

empirical_hrf <- function(t, y, name = "empirical_hrf") { as_hrf(approxfun(t, y, yright = 0, yleft = 0), name = name, nbasis = 1, span = max(t)) }

Exactly the same public behaviour, just written with the single constructor. If we want to keep the old name for backward compatibility:

gen_empirical_hrf <- function(...) { .Deprecated("empirical_hrf()") empirical_hrf(...) }

2 Basis‑set combiner → bind_basis()

old ------------------------------------------------------

gen_hrf_set <- function(..., span = 32, name = "hrf_set") { … }

new ------------------------------------------------------

hrf_set <- function(..., name = "hrf_set") { bind_basis(...) |> ((h) { attr(h, "name") <- name; h })() }

All the busywork (checking each element is a function, cbind‑eval, computing nbasis and span) already lives inside bind_basis(). Again, deprecate the alias if you wish:

gen_hrf_set <- function(...) { .Deprecated("hrf_set()") hrf_set(...) }

3 Parameter‑grid library → one tidy‑map call

old ------------------------------------------------------

gen_hrf_library <- function(fun, pgrid, ...) { … }

new ------------------------------------------------------

hrf_library <- function(fun, pgrid, ...) { purrr::pmap(pgrid, (...) fun |> purrr::partial(...) |> as_hrf(...)) |> ((fs) do.call(bind_basis, fs))() }

Differences / improvements

Aspect Old code New one‑liner Handles arbitrary parameter names? yes yes (pmap) Carries each HRF's own attributes? partly yes (as_hrf() inside the loop) Combines into basis set? ad‑hoc gen_hrf_set() bind_basis() Lines of code ~25 5

Deprecation wrapper:

gen_hrf_library <- function(...) { .Deprecated("hrf_library()") hrf_library(...) }

Minimal impact checklist ✔︎

Down‑stream function Still works? Why evaluate() ✔︎ All objects inherit function + "HRF", attributes unchanged. nbasis(), print.* ✔︎ Attributes set by as_hrf() / bind_basis(). Formula DSL hrf(cond, basis = …) ✔︎ make_hrf() calls as_hrf()‑based constructors. Legacy code ✔︎ w/ message Compatibility shims using .Deprecated().

Take‑away

Those "odds and ends" collapse to ≤ 20 tidy lines once the single‑source primitives (as_hrf(), bind_basis()) exist. This keeps the public API intact and enforces the one constructor / one decorator rule we set for maximal elegance and maintainability.



bbuchsbaum/fmrireg documentation built on June 10, 2025, 8:18 p.m.