Skip to content

tfd.tf rejects function-valued evaluator (only accepts symbol or string) #261

@fabian-s

Description

@fabian-s

Discovered while fixing #238. tfd.tf (and the new_tfd evaluator resolution) accepts evaluator as either a bareword symbol (resolved via match.call/as_name) or a string (resolved via get). A function value passed by reference fails.

library(tf)
x <- tf_rgp(3)
fn <- tf_approx_spline
tfd(x, evaluator = fn)              # fails
tfd(x, evaluator = function(x, arg, evaluations) ...)  # fails — anonymous

The proper fix lives in tfd.tf (or the new_tfd evaluator resolution path), not in upstream callers. PR #238 originally worked around this by adding a match.call coercion in tf_rebase.tfb.tfd, but that workaround has gaps: it handles a bare symbol but silently fails for tf:::tf_approx_spline (namespace-qualified ::/::: call), anonymous function(x, ...) ..., or any expression. The fix should be at the right layer.

Suggested fix

In R/tfd-class.R's new_tfd (or its evaluator-resolution helper, currently at lines ~24-37), additionally handle the case where the supplied evaluator is already a function:

if (is.function(evaluator)) {
  evaluator_fn   <- evaluator
  evaluator_name <- if (is.null(attr(evaluator, "evaluator_name")))
                       deparse(substitute(evaluator)) else attr(evaluator, "evaluator_name")
  # but substitute() here is in the wrong frame — see the existing deparse/get dance
}

Care needed: substitute()/deparse() won't give a clean name for tf:::tf_approx_spline or anonymous functions. Acceptable behaviors: (a) require a string for the name and accept a function for the value (split into evaluator + evaluator_name), (b) store the function and synthesize a default evaluator_name like "<custom>".

Regression test (add when fixing)

test_that("tfd.tf accepts function-valued evaluator", {
  x <- tf_rgp(3)
  expect_no_error(y1 <- tfd(x, evaluator = tf_approx_spline))
  expect_no_error(y2 <- tfd(x, evaluator = function(x, arg, evaluations) {
    # simple linear interp via approx
    approx(x = arg, y = evaluations, xout = x, rule = 2)$y
  }))
  expect_equal(tf_evaluations(y1), tf_evaluations(tfd(x, evaluator = "tf_approx_spline")))
})

Found while reviewing PR for #238. The workaround in tf_rebase.tfb.tfd should be reverted once this is fixed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions