AgentStack
SKILL unreviewed MIT Self-run

Metaprogramming

skill-jsperger-llm-r-skills-metaprogramming · by jsperger

>

No reviews yet
0 installs
8 views
0.0% view→install

Install

$ agentstack add skill-jsperger-llm-r-skills-metaprogramming

Open-source listing — not yet scanned by AgentStack. Follow the source repository for install instructions.

Security review

⚠ Flagged

1 finding(s); flagged for manual review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures
  • high Dangerous shell/eval execution.

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution Used

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Metaprogramming? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

R Metaprogramming with rlang

Metaprogramming is the ability to defuse, create, and inject R expressions. The core pattern is defuse-and-inject: capture code as data, optionally transform it, then inject it into another context for evaluation.

Quick Reference

| Task | Function/Operator | |------|-------------------| | Defuse your own expression | expr(x + 1) | | Defuse user's single argument | enquo(arg) | | Defuse user's ... arguments | enquos(...) | | Inject single expression | !! or {{ | | Splice list of expressions | !!! | | Get expression from quosure | quo_get_expr(q) | | Get environment from quosure | quo_get_env(q) | | Build symbol from string | sym("name") | | Build symbol with .data pronoun | data_sym("name") | | Build symbols from vector | syms(names) / data_syms(names) | | Auto-label expression | as_label(quo) | | Format argument as string | englue("{{ x }}") | | Interpolate name in dynamic dots | "{name}" := value | | Interpolate argument in name | "{{ arg }}" := value |

Defusing Expressions

Defusing stops evaluation and returns the expression as a tree-like object (a "blueprint" for computation).

# Normal evaluation returns result
1 + 1
#> [1] 2

# Defusing returns the expression
expr(1 + 1)
#> 1 + 1

expr() vs enquo()

| Function | Defuses | Returns | Use When | |----------|---------|---------|----------| | expr() | Your own code | Expression | Building expressions locally | | enquo() | User's argument | Quosure | Forwarding function arguments | | enquos() | User's ... | List of quosures | Forwarding multiple arguments |

# Defuse your own expression
my_expr  
#> expr: ^cyl + am
#> env:  global

enquos() with .named

Auto-label unnamed arguments:

g  [1] "cyl"   "1 + 1"

g(foo = cyl, bar = 1 + 1)
#> [1] "foo" "bar"

Types of Defused Expressions

  • Calls: f(x, y), 1 + 1 - function invocations
  • Symbols: x, df - named object references
  • Constants: 1, "text", NULL - literal values

Quosures

A quosure wraps an expression with its original environment. This is critical for correct evaluation when expressions travel across function and package boundaries.

Why Environments Matter

# In package A
my_function  dplyr::summarise({{ var }})
}

my_summarise  dplyr::summarise(!!enquo(var))
}

Use {{ when you simply need to forward an argument. Use enquo() + !! when you need to inspect or transform the expression first.

!! (Bang-Bang)

Injects a single expression:

var  dplyr::summarise(mean(!!var))
#> Equivalent to: summarise(mean(cyl))

# Inject a value to avoid name collisions
x  dplyr::mutate(x = x / !!x)
#> Uses column x divided by env value 100

!!! (Splice)

Injects each element of a list as separate arguments:

vars  dplyr::select(!!!vars)
#> Equivalent to: select(cyl, am, vs)

# With enquos()
my_group_by  dplyr::group_by(!!!enquos(...))
}

Where Operators Work

  • Data-masked arguments: Implicitly enabled (dplyr, ggplot2, etc.)
  • inject(): Explicitly enables operators in any context
  • Dynamic dots: !!! and {name} work in functions using list2()
# Enable injection in base functions
inject(
  with(mtcars, mean(!!sym("cyl")))
)

Building Expressions from Data

sym() and syms()

Convert strings to symbols:

var  cyl

vars  [[1]]
#> cyl
#> [[2]]
#> am

datasym() and datasyms()

Create .data$col expressions (safer in tidy eval, avoids collisions):

data_sym("cyl")
#> .data$cyl

data_syms(c("cyl", "am"))
#> [[1]]
#> .data$cyl
#> [[2]]
#> .data$am

Use sym() for base R functions; use data_sym() for tidy eval functions.

Building Calls

# With call()
call("mean", sym("x"), na.rm = TRUE)
#> mean(x, na.rm = TRUE)

# With expr() and injection
var  mean(x, na.rm = TRUE)

Name Interpolation (Glue Operators)

In dynamic dots, use glue syntax for names.

{ for Variable Values

name  # A tibble: 3 x 1
#>     foo
#>   
#> 1     1
#> 2     2
#> 3     3

tibble::tibble("prefix_{name}" := 1:3)
#> Column named: prefix_foo

{{ for Argument Labels

my_mutate  dplyr::mutate("mean_{{ var }}" := mean({{ var }}))
}
mtcars |> my_mutate(cyl)
#> Creates column: mean_cyl

englue() for String Formatting

my_function  [1] "Column: some_column"

Advanced: Manual Expression Transformation

When you need to modify expressions before injection:

my_mean  dplyr::summarise(mean = !!wrapped)
}

For multiple arguments:

my_mean  dplyr::summarise(!!!vars)
}

Base R Equivalents

| rlang | Base R | Notes | |-------|--------|-------| | expr() | bquote() | bquote uses .() for injection | | enquo() | substitute() | substitute returns naked expr, not quosure | | enquos(...) | eval(substitute(alist(...))) | Workaround for dots | | !! | .() in bquote | Only inside bquote | | eval_tidy() | eval() | eval_tidy supports .data/.env pronouns |

Pitfalls

{{ on Non-Arguments

{{ should only wrap function arguments. On regular objects, it captures the value, not the expression:

# Correct: var is a function argument
my_fn <- function(var) {{ var }}

# Problematic: x is not an argument
x <- 1
{{ x }}  # Returns 1, not the expression

Operators Out of Context

Outside tidy eval/inject contexts, operators have different meanings:

| Operator | Intended | Outside Context | |----------|----------|-----------------| | {{ | Embrace | Double braces (returns value) | | !! | Inject | Double negation (logical) | | !!! | Splice | Triple negation (logical) |

These fail silently. See the [tidy-evaluation](../tidy-evaluation/SKILL.md) skill for details on proper usage contexts.

See Also

  • tidy-evaluation: Programming patterns for data-masked functions
  • designing-tidy-r-functions: Function API design principles
  • rlang-conditions: Error handling with rlang

Reference Files

  • [topic-quosure.md](topic-quosure.md) - Complete quosure reference
  • [topic-metaprogramming.md](topic-metaprogramming.md) - Advanced transformation patterns
  • [topic-multiple-columns.md](topic-multiple-columns.md) - Multiple columns patterns

Vignettes

Access detailed rlang documentation via R:

# Defusing expressions
vignette("topic-defuse", package = "rlang")

# Injection operators
vignette("topic-inject", package = "rlang")

# Or browse all vignettes
browseVignettes("rlang")

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.