---
title: "Getting Started"
output: arl::arl_html_vignette
pkgdown:
as_is: true
vignette: >
%\VignetteIndexEntry{Getting Started}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r setup, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
arl::register_knitr_engine()
```
Arl is a Lisp dialect that compiles to R. Every R function and data structure
is available directly, and the macro system lets you transform code at compile
time. This vignette walks through installing Arl, running the REPL, and
writing your first expressions.
## Installation
```{r eval=FALSE}
# install.packages("arl")
devtools::install_github("wwbrannon/arl")
```
Arl is not on CRAN at the time of writing; install from GitHub with `devtools`
as shown above. Once a CRAN release is available, the package can also be
installed with the built-in `install.packages` function. If you have a local
clone of the repository, `devtools::install()` from the repo directory will
also work.
## Start the REPL
```{r eval=FALSE}
engine <- arl::Engine$new()
engine$repl()
```
```{r, include=FALSE}
engine <- arl::Engine$new()
stopifnot(is.function(engine$repl))
```
The engine loads the prelude automatically (though this [can be
customized](#loading-stdlib-modules)), so functions like `when`, `let`, `map`,
and `->` are available immediately.
At the prompt, you can enter Arl expressions:
```{arl}
(+ 1 2)
(define x 10)
(* x 2)
```
```{arl, include=FALSE}
(assert-equal 3 (+ 1 2))
(assert-equal 20 (* x 2))
```
Type `(quit)` or press Ctrl+C to exit the REPL.
**REPL options:** You can control the REPL with options before calling
`engine$repl()`: `arl.repl_quiet` (minimal banner),
`arl.repl_use_history` (set to `FALSE` to avoid touching R's readline
history), and `arl.repl_bracketed_paste` (enable/disable bracketed paste
mode for cleaner multiline pastes). See the discussion of [runtime
options](runtime-options.html) for more.
## Run from the command line
Run `arl::install_cli()` to see how to put the CLI wrapper on your PATH:
```{r eval=FALSE}
arl::install_cli()
#> Arl CLI wrapper script: /path/to/arl/bin/posix/arl
#>
#> To make it available on your PATH, create a symlink:
#>
#> mkdir -p ~/.local/bin
#> ln -s "/path/to/arl/bin/posix/arl" ~/.local/bin/arl
#>
#> Then ensure ~/.local/bin is on your PATH.
```
Then you can evaluate code without opening R:
```bash
arl --eval "(+ 1 2)"
arl --file script.arl
arl -q # quiet REPL (minimal banner)
arl --help # see all options
```
You can also pass multiple files; they run in order in a shared engine, so
definitions from earlier files are visible to later ones. Use
`--no-stdlib` to start a bare engine without stdlib modules.
## Evaluate Arl from R
The simplest way to evaluate Arl inside an R script is `eval_text` (or its
alias `eval_string`), which reads and evaluates in one step:
```{r eval=FALSE}
engine <- arl::Engine$new()
engine$eval_text("(define x 10) (+ x 5)")
engine$eval_string("(define y 20) (+ y 5)")
```
For finer control, you can parse and evaluate separately:
```{r eval=FALSE}
exprs <- engine$read("(define x 10) (+ x 5)")
engine$eval(exprs[[1]], exprs[[2]])
```
```{r, include=FALSE}
engine <- arl::Engine$new()
stopifnot(identical(engine$eval_text("(define x 10) (+ x 5)"), 15))
stopifnot(identical(engine$eval_string("(define y 20) (+ y 5)"), 25))
exprs <- engine$read("(define z 10) (+ z 5)")
stopifnot(length(exprs) == 2L)
stopifnot(identical(engine$eval(exprs[[1]], exprs[[2]]), 15))
```
`eval()` accepts multiple expressions and evaluates them sequentially,
returning the last value.
## Passing R data to the engine
Use `$define()` to inject R objects into the engine so Arl code can use them:
```{r eval=FALSE}
engine <- arl::Engine$new()
engine$define("my_data", mtcars)
engine$eval_text("(head my_data 3)")
```
```{r, include=FALSE}
engine <- arl::Engine$new()
engine$define("my_data", mtcars)
stopifnot(identical(
engine$eval_text("(nrow my_data)"),
nrow(mtcars)
))
```
Calls to `$define()` return the engine invisibly, so they can be chained:
```{r eval=FALSE}
engine$define("x", 10)$define("y", 20)$eval_text("(+ x y)")
```
```{r, include=FALSE}
stopifnot(identical(
engine$define("x", 10)$define("y", 20)$eval_text("(+ x y)"),
30
))
```
To read results back into R, use `$eval_text()` (which returns the last
value) or `$get_env()` to access the engine's environment directly.
## Run Arl files
From the REPL, use `load` to run a file in the current environment so that
its definitions are visible:
```{arl, eval=FALSE}
(load "script.arl")
```
Use `run` to execute a file in an isolated child environment (definitions
are not visible to later code):
```{arl, eval=FALSE}
(run "script.arl")
```
```{arl, include=FALSE}
(define gs-load-path (tempfile :fileext ".arl"))
(writeLines (c "(define gs-load-visible 99)") gs-load-path)
(load gs-load-path)
(assert-equal 99 gs-load-visible)
(define gs-run-path (tempfile :fileext ".arl"))
(writeLines (c "(define gs-run-only 123)") gs-run-path)
(run gs-run-path)
(assert-equal "missing" (try-catch gs-run-only (catch e "missing")))
(unlink gs-load-path)
(unlink gs-run-path)
```
From R, you can mirror the two forms with `load_file_in_env`:
```{r eval=FALSE}
engine <- Engine$new()
# Like (load ...): definitions visible in the engine
engine$load_file_in_env("script.arl")
# Like (run ...): isolated; definitions not visible
engine$load_file_in_env("script.arl", new.env(parent = engine$get_env()))
```
```{r, include=FALSE}
engine <- arl::Engine$new()
script_path <- tempfile(fileext = ".arl")
writeLines("(define gs-file-value 99)", script_path)
script_path <- normalizePath(script_path, winslash = "/", mustWork = TRUE)
engine$load_file_in_env(script_path)
stopifnot(identical(engine$eval_text("gs-file-value"), 99))
engine2 <- arl::Engine$new()
engine2$load_file_in_env(script_path, new.env(parent = engine2$get_env()))
stopifnot(inherits(try(engine2$eval_text("gs-file-value"), silent = TRUE), "try-error"))
unlink(script_path)
```
If you don't specify an environment, `load_file_in_env` uses the engine's
toplevel environment.
## Loading stdlib modules
The engine loads the **prelude** automatically — 10 modules (`logic`, `core`,
`types`, `list`, `equality`, `functional`, `control`, `sequences`, `binding`,
`threading`) whose exports are available immediately. Functions like `when`,
`let`, `->`, and `try` come from the prelude and need no import.
`do-list` requires `(import looping :refer :all)`.
Non-prelude modules must be loaded explicitly with `import`. A bare
`(import X)` makes the module available for qualified access (e.g.
`math/inc`); add `:refer :all` to also import all exports unqualified:
```{arl, eval=FALSE}
(import math) ; math/inc, math/dec, ...
(import math :refer :all) ; inc/dec/abs/clamp/... (unqualified)
(import looping :refer :all) ; loop/recur/until
(import sort :refer :all) ; list-sort/sort-by/...
(import strings :refer :all) ; string-split/string-join/...
```
Imports can also be selective or aliased:
```{arl, eval=FALSE}
(import control :refer (when unless))
(import strings :as str) ; str/string-split, str/string-join, ...
```
Prelude modules are already available, but you can still import them
explicitly in your own modules (which start with an empty scope plus prelude
access):
```{arl, eval=FALSE}
(import control :refer :all) ; when/unless/cond/case/try/try-catch/call-cc
```
```{arl, include=FALSE}
(import looping :refer :all)
(assert-true (macro? 'when))
(assert-true (macro? 'unless))
(assert-true (macro? 'cond))
(assert-true (macro? 'let))
(assert-true (macro? 'loop))
(assert-true (macro? '->))
(assert-true (macro? 'try-catch))
```
See [Modules and Imports](modules.html) for details on creating and importing
modules, and the [Language Reference](lang-reference.html) for a list of stdlib
modules and the full set of functions/macros each provides.
## R functions are available directly
Because Arl compiles to R and its environment chain parents to R's
`baseenv()`, every function in R's base package is available without any
import. Functions like `max`, `min`, `sum`, `c`, `length`, `paste`, `lapply`,
and hundreds of others work as-is:
```{arl}
(max 1 5 3)
(length (c 10 20 30))
(paste "hello" "world" :sep ", ")
```
```{arl, include=FALSE}
(assert-equal 5 (max 1 5 3))
(assert-equal 3 (length (c 10 20 30)))
(assert-equal "hello, world" (paste "hello" "world" :sep ", "))
```
Arl shadows some R names with its own versions (arithmetic operators are
variadic, comparisons chain, `=` is equality not assignment). R's default
packages (`stats`, `utils`, `grDevices`, `graphics`, `datasets`, `methods`)
are also attached automatically, so functions like `median`, `head`, `lm`,
and data like `iris` work without a prefix. See
[Inherited R Functions](lang-reference.html#inherited-r-functions) in the
Language Reference for details, and
[R Interop and Data Workflows](r-interop.html) for calling R packages,
using keyword arguments, and `r-eval`.
## Core syntax and semantics
### Truthiness
Arl follows R's truthiness rules: `#f`/`FALSE`, `#nil`/`NULL`, and `0` are
falsy; everything else is truthy. This differs from Scheme, where only `#f`
is falsy. See [Troubleshooting](troubleshooting.html) for common pitfalls.
### Definitions and functions
```{arl}
; Define a variable
(define greeting "hello")
; Define a function
(define factorial
(lambda (n)
(if (< n 2)
1
(* n (factorial (- n 1))))))
(factorial 5)
```
```{arl, include=FALSE}
(assert-equal "hello" greeting)
(assert-equal 120 (factorial 5))
```
Use `unbind-variable` (which ultimately relies on R's `rm`) to remove a
binding:
```{arl}
(define tmp 42)
(unbind-variable 'tmp (current-env))
```
```{arl, include=FALSE}
(define ub-gs-test 1)
(unbind-variable 'ub-gs-test (current-env))
(assert-equal "gone" (try-catch ub-gs-test (catch e "gone")))
```
### Local bindings
```{arl}
; let binds variables in a local scope
(let ((x 10)
(y 20))
(+ x y))
```
```{arl, include=FALSE}
(assert-equal 30 (let ((x 10) (y 20)) (+ x y)))
```
### Conditionals
```{arl}
; if is the basic conditional
(if (> 3 2) "yes" "no")
; cond handles multiple branches
; the fallback #t case is like "else"
(define describe
(lambda (n)
(cond
((< n 0) "negative")
((= n 0) "zero")
(#t "positive"))))
(describe 5)
; when is a one-armed conditional (no else branch)
(when (> 3 2)
(print "3 is greater"))
```
```{arl, include=FALSE}
(assert-equal "yes" (if (> 3 2) "yes" "no"))
(assert-equal "positive" (describe 5))
(assert-equal "negative" (describe -1))
(assert-equal "zero" (describe 0))
```
### Sequencing
```{arl}
; begin evaluates expressions in order
; the last value is returned
(begin
(define a 1)
(define b 2)
(+ a b))
```
```{arl, include=FALSE}
(assert-equal 3 (begin (define a 1) (define b 2) (+ a b)))
```
### Lists and quoting
Code and data in Arl, as in Lisp generally, are made of lists. You can define
and manipulate lists:
```{arl}
; Comments start with semicolon
(list 1 2 3)
(car (list 1 2 3)) ; car = first element
(cdr (list 1 2 3)) ; cdr = rest of list after car
```
```{arl, include=FALSE}
(assert-equal (list 1 2 3) (list 1 2 3))
(assert-equal 1 (car (list 1 2 3)))
(assert-equal (list 2 3) (cdr (list 1 2 3)))
```
Lists typed in at the prompt are, by default, evaluated. To prevent evaluation,
use the special form `quote` or its syntactic sugar `'`:
```{arl}
(quote (+ 1 2)) ; => unevaluated expression (R language object / call)
'(+ 1 2) ; simple alias for (quote ...)
(list? '(+ 1 2))
(base::is.list '(+ 1 2))
```
```{arl, include=FALSE}
(assert-equal '(+ 1 2) (quote (+ 1 2)))
(assert-equal '(+ 1 2) '(+ 1 2))
(assert-true (list? '(+ 1 2)))
(assert-false (base::is.list '(+ 1 2)))
```
This is an important Arl-vs-R distinction: `list?` follows Arl's Lisp-style
semantics and treats quoted forms (which are [R call/language
objects](https://cran.r-project.org/doc/manuals/r-devel/R-lang.html#Computing-on-the-language))
as lists, while R's `base::is.list` reports the underlying R object type and
returns `FALSE` for calls.
You can also perform selective evaluation with the `quasiquote` template syntax
(written with the backtick `` ` ``), which is widely used in defining
[macros](macros.html):
```{arl}
; Quasiquote allows selective evaluation with , and ,@
(define x 10)
(define y (list 1 2 3))
`(list ,x 20 30) ; => (list 10 20 30) -- x is substituted, rest is literal
`(list ,@y 20 30) ; => (list 1 2 3 20 30) -- y is spliced in at same level
```
```{arl, include=FALSE}
(define x 10)
(define y (list 1 2 3))
(assert-equal '(list 10 20 30) `(list ,x 20 30))
(assert-equal '(list 1 2 3 20 30) `(list ,@y 20 30))
```
For convenience, an Arl list is also an R list under the hood:
```{arl}
(base::is.list (list 1 2 3))
```
```{arl, include=FALSE}
(assert-true (base::is.list (list 1 2 3)))
```
Lisp's traditional pair lists made of cons cells are also supported, though
they are implemented differently and less commonly used:
```{arl}
(define pl (3 . (4 . 5))) ; this is a dotted-pair list
(pair? pl)
(list? pl) ; pair lists are a different kind of object
```
```{arl, include=FALSE}
(define pl (3 . (4 . 5)))
(assert-true (pair? pl))
(assert-false (list? pl))
```
For a concise guide to R-style lists vs dotted pairs and when to use each, see
[Pairlists vs R lists](arl-vs-scheme.html#pairlists-vs-r-lists).
For more on quoted forms as Arl lists but R language objects, see
[Pairlists vs R lists](arl-vs-scheme.html#pairlists-vs-r-lists) and
[R eval and calls](r-interop.html#r-eval).
For more details on the standard library and macros, see the other vignettes in
this package.
## Getting help
Arl has a built-in help system. Use `help` with a string to look up any
special form, macro, built-in, or stdlib function:
```{arl, eval=FALSE}
(help "define") ; special form
(help "when") ; macro -- shows docs and usage
(help "map") ; stdlib function
```
```{arl, include=FALSE}
(assert-equal "ok" (try-catch (begin (help "define") "ok") (catch e "bad")))
(assert-equal "ok" (try-catch (begin (help "when") "ok") (catch e "bad")))
(assert-equal "ok" (try-catch (begin (help "map") "ok") (catch e "bad")))
```
For functions or macros with documentation, `help` shows the signature, description,
examples, and cross-references automatically. It also falls through to R's
built-in help for R functions.
### Documenting your functions
Arl provides two mechanisms for attaching documentation to functions and
macros:
- **`;;'` annotation comments** — Place roxygen-like tags (`@description`,
`@examples`, `@seealso`, `@note`, etc) immediately before a `define` or
`defmacro`. The compiler bakes the documentation in at compile time with
no runtime overhead. This is the recommended approach for source files.
- **`doc!`** — Attach or update documentation at runtime, useful for
interactive work: `(doc! my-fn "Description here.")` or with keyword
arguments like `(doc! my-fn :examples "(my-fn 3)")`.
Both produce the same `arl_doc` attribute, so `help` and `doc` work
identically regardless of how documentation was attached. See
[Documenting Functions and Macros](documenting-functions-macros.html) for the full reference.
## Next steps
- [Functions](functions.html)
- [Modules and Imports](modules.html)
- [Macros and Quasiquote](macros.html)
- [R Interop and Data Workflows](r-interop.html)
- [Examples](examples.html)
- [Troubleshooting](troubleshooting.html)
- [Arl Compared to Scheme](arl-vs-scheme.html)