Arl can call any R function directly. Because Arl code is compiled to
R and the environment chain includes R’s baseenv() and
exports from R’s default packages (stats,
utils, grDevices, graphics,
datasets, methods), functions like
max, sum, median,
head, lm, and data like iris all
work without imports or package prefixes. See Inherited R
Functions in the Language Reference for a survey of commonly used
inherited functions and details on which names Arl shadows with its own
versions.
Calling R functions
arl> (mean (c 1 2 3 4 5))
#> 3
arl> (seq :from 1 :to 10 :by 2)
#> 1 3 5 7 9
Keywords (:from, :to) map to named
arguments in R.
Keyword syntax
Keywords starting with : serve a dual purpose in
Arl:
-
As named argument syntax: Bare keywords in function calls become named arguments
arl> (seq :from 1 :to 10) ; R: seq(from = 1, to = 10) #> 1 2 3 4 5 6 7 8 9 10 -
As self-evaluating values: Keywords are first-class values that can be bound to variables
arl> :keyword ; evaluates to a keyword object #> "keyword" arl> (define k :foo) ; bind a keyword to a variable #> "foo"
To pass a keyword as a value (rather than as named argument syntax), quote it:
(equal? ':foo ':bar) ; compares two keyword values → #f
(equal? ':foo ':foo) ; → #t
(equal? :foo :bar) ; error: bare keywords become named argumentsThis design allows keywords to work naturally with R’s named arguments while remaining first-class values when needed.
r-call
Most R functions can be called directly from Arl
(e.g. (mean x)). r-call is useful in two
situations: (1) when an Arl binding has shadowed an R
function name and you need to reach the original, and (2) when the
function name is computed at runtime (stored in a
variable as a string). It takes a character string, skips all
Arl-defined evaluation frames, and looks up the name in the containing R
scopes:
arl> ; shadow the binding so direct calls fail
arl> (define sum (lambda () (stop "error")))
#> <function>
arl> ; r-call can still find it
arl> (r-call "sum" (list 1 2 3))
#> 6
arl> ; avoid keeping this shadowing binding around (see below)
arl> (unbind-variable 'sum (current-env))
r-eval
r-eval evaluates an R expression directly via R’s
eval(), bypassing Arl’s compiler entirely. Where
r-call looks up a function by name and calls it with
pre-evaluated arguments, r-eval takes an unevaluated call
expression and hands it to R as-is:
arl> (r-eval (quote (seq_len 5))) ; => c(1, 2, 3, 4, 5)
#> 1 2 3 4 5
Note: these quoted forms are R language/call objects. Arl’s
list? treats them as lists for Lisp semantics, while R’s
base::is.list reports FALSE.
Use Arl’s call function to build the expression
programmatically, and pass (current-env) so R can see your
local bindings:
arl> (define n 3)
#> 3
arl> (r-eval (call (list 'seq_len 'n)) (current-env)) ; => c(1, 2, 3)
#> 1 2 3
r-eval is the escape hatch for R constructs that Arl
normally overrides (for, while) or that
require non-standard evaluation (see below).
Non-standard evaluation
Some R functions use non-standard evaluation (NSE) — they capture
unevaluated expressions and evaluate them in a controlled context.
Examples include tryCatch, with,
within, and subset.
r-call can’t help here because it evaluates arguments
before passing them to the R function (via
do.call). r-eval avoids this: you build an
unevaluated call expression and let R evaluate it, so the NSE function
receives exactly the expression it expects.
;; WRONG - thunk is evaluated before tryCatch can catch errors
(r-call "tryCatch"
(list :expr (thunk) :error (lambda (e) #t)))
;; CORRECT - build unevaluated expression, then eval it
(define thunk-expr (call (list thunk))) ; unevaluated call
(r-eval (call (list 'tryCatch thunk-expr ; 'tryCatch = quoted symbol
:error (lambda (e) #t)))
(current-env))Key points:
- Use Arl’s
callfunction to build unevaluated call expressions - Use a quoted symbol (
'tryCatch) not a string ("tryCatch") - Use
r-eval(notr-call) to evaluate the constructed expression - Pass
(current-env)as the evaluation environment
This pattern is used throughout the stdlib — see try in
control.arl and assert-error in
assert.arl for working examples.
substitute
R’s substitute(expr) (the single-argument form) captures
the unevaluated expression passed to a function. This doesn’t work in
Arl: Arl evaluates arguments before calling functions, so there is no
unevaluated expression to capture. Use macros instead — a macro’s body
receives its arguments as unevaluated syntax, which is the same thing
substitute provides in R:
arl> ;; In R you might write:
arl> ;; log_call <- function(expr) { cat(deparse(substitute(expr))); expr }
arl> ;;
arl> ;; In Arl, use a macro:
arl> (defmacro log-call (expr)
arl> `(begin
arl> (cat (deparse ',expr) "\n")
arl> ,expr))
arl> (log-call (+ 1 2))
#> 1 + 2
#> 3
The two-argument form (substitute expr env)
does work — it performs substitution on
expr using bindings from env, which doesn’t
require capturing the caller’s expression:
arl> (substitute '(+ a b) (list :a 1 :b 2)) ; => (+ 1 2)
#> (+ 1 2)
Working with formulas
arl> (lm (~ mpg cyl) :data mtcars)
#>
#> Call:
#> lm(formula = mpg ~ cyl, data = mtcars)
#>
#> Coefficients:
#> (Intercept) cyl
#> 37.885 -2.876
The ~ operator builds an R formula without evaluating
its arguments.
Accessing R objects
arl> (define mylist (list :a 1 :b 2))
#> (:a 1 :b 2)
arl> ($ mylist "a") ; => 1
#> 1
arl> (define vector (c 1 2 3))
#> 1 2 3
arl> ([ vector 1) ; => 1
#> 1
Use :: and ::: to access R packages:
arl> (:: stats median)
#> <function>
arl> (::: stats .lm.fit)
#> <function>
R base operators are also available directly:
arl> (^ 2 3)
#> 8
arl> (%/% 7 3)
#> 2
Managing bindings
Use get to retrieve a binding by name and
unbind-variable to remove one. Both accept an optional
environment argument (defaulting to .GlobalEnv for
get and (current-env) for
unbind-variable):
arl> (define temp-binding 42)
#> 42
arl> (get 'temp-binding (current-env)) ; => 42
#> 42
arl> (unbind-variable 'temp-binding (current-env))
arl> ; temp-binding is no longer defined
Names can also be passed as strings:
Passing data between R and Arl
Use $define() to inject R objects into the engine:
engine <- Engine$new()
engine$define("my_data", mtcars)
engine$eval_text("(nrow my_data)") # => 32To read results back into R, use $eval_text() (returns
the last value) or access the engine’s environment with
$get_env():
result <- engine$eval_text("(median ($ my_data \"mpg\"))")
env <- engine$get_env()
ls(env) # see all bindingsExample workflow
The data analysis example
shows a small analysis pipeline from Arl that uses
data.frame, mean, and lm. You can
run it with:
engine <- Engine$new()
engine$load_file_in_env(system.file("examples", "data-analysis.arl", package = "arl"))Standard library helpers
The stdlib exposes helpers like map,
filter, reduce, and threading macros
(->, ->>). See the Language Reference for the full list.