---
title: "Functions"
output: arl::arl_html_vignette
pkgdown:
as_is: true
vignette: >
%\VignetteIndexEntry{Functions}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r setup, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
arl::register_knitr_engine()
```
This vignette covers how to define, call, and compose functions in Arl.
## Defining Functions
Functions are created with `lambda` and bound to names with `define`:
```{arl}
(define double
(lambda (x)
(* x 2)))
(double 5)
```
```{arl, include=FALSE}
(assert-equal 10 (double 5))
```
**Important:** Unlike Scheme, `(define (f x) body)` is **not** function
shorthand in Arl. Because `define` supports
[destructuring](#destructuring), writing `(define (f x) body)` tries to
destructure the value `body` into the pattern `(f x)` — it does not
create a function. Always use the explicit `define` + `lambda` form
shown above.
```{arl, eval=FALSE}
;; WRONG — this is destructuring, not function definition
(define (f x) (+ x 1))
;; CORRECT
(define f (lambda (x) (+ x 1)))
```
```{arl, include=FALSE}
;; Verify the wrong pattern errors (destructuring bind mismatch)
(assert-error (eval '(define (f x) (+ x 1))))
```
## Parameter Features {#parameter-features}
Arl's `lambda` supports several parameter styles. These work identically
in `defmacro` parameters.
### Required parameters
```{arl}
(define add (lambda (a b) (+ a b)))
(add 3 4)
```
```{arl, include=FALSE}
(assert-equal 7 (add 3 4))
```
### Optional parameters with defaults
Wrap a parameter in a pair `(name default)`:
```{arl}
(import strings :refer (string-append))
(define greet
(lambda ((name "world"))
(string-append "hello, " name)))
(greet) ; uses default
(greet "Alice") ; overrides default
```
```{arl, include=FALSE}
(assert-equal "hello, world" (greet))
(assert-equal "hello, Alice" (greet "Alice"))
```
### Rest parameters
Use `.` to collect remaining arguments into a list:
```{arl}
(define sum-all
(lambda (first . rest)
(reduce + (cons first rest))))
(sum-all 1 2 3 4)
```
```{arl, include=FALSE}
(assert-equal 10 (sum-all 1 2 3 4))
```
### Destructuring parameters {#destructuring-parameters}
Use `(pattern ...)` to destructure an argument:
```{arl}
(define first-of-pair
(lambda ((pattern (a b)))
a))
(first-of-pair (list 10 20))
```
```{arl, include=FALSE}
(assert-equal 10 (first-of-pair (list 10 20)))
```
Patterns can be nested, have defaults, or combine with rest parameters:
```{arl}
(define point-sum
(lambda ((pattern (x y) (list 0 0)))
(+ x y)))
(point-sum) ; uses default (0 0)
(point-sum (list 3 4)) ; => 7
```
```{arl, include=FALSE}
(assert-equal 0 (point-sum))
(assert-equal 7 (point-sum (list 3 4)))
```
### Combining parameter styles
You can mix required, optional, destructuring, and rest parameters:
```{arl}
(define flexible
(lambda (required (opt 10) . rest)
(list required opt rest)))
(flexible 1) ; => (1 10 ())
(flexible 1 2 3 4) ; => (1 2 (3 4))
```
```{arl, include=FALSE}
(assert-equal (list 1 10 (list)) (flexible 1))
(assert-equal (list 1 2 (list 3 4)) (flexible 1 2 3 4))
```
## Destructuring {#destructuring}
Destructuring lets you unpack a data structure into individual variables
in a single `define` statement. Instead of binding a name to a value,
you provide a **pattern** — a nested list of names — and Arl matches
each name to the corresponding element of the value:
```{arl}
(define (x y z) (list 10 20 30))
(list x y z)
```
```{arl, include=FALSE}
(assert-equal 10 x)
(assert-equal 20 y)
(assert-equal 30 z)
```
This works with nested structures too:
```{arl}
(define (p (q r)) (list 1 (list 2 3)))
(list p q r)
```
```{arl, include=FALSE}
(assert-equal 1 p)
(assert-equal 2 q)
(assert-equal 3 r)
```
### `destructuring-bind`
The `destructuring-bind` macro provides a scoped form of destructuring.
It binds a pattern to a value and evaluates body forms with those
bindings in scope:
```{arl}
(destructuring-bind (first second . rest) (list 1 2 3 4 5)
(list first second rest))
```
```{arl, include=FALSE}
(assert-equal (list 1 2 (list 3 4 5))
(destructuring-bind (first second . rest) (list 1 2 3 4 5)
(list first second rest)))
```
This is what macros like `let*` and `when-let` use under the hood — each
binding in a `let` form is a destructuring bind, so patterns work anywhere a
`let` binding does:
```{arl}
(let (((a b) (list 1 2))
((c d) (list 3 4)))
(+ a b c d))
```
```{arl, include=FALSE}
(assert-equal 10 (let (((a b) (list 1 2))
((c d) (list 3 4)))
(+ a b c d)))
```
### Destructuring in function parameters
As shown in [Parameter Features](#parameter-features) above, `lambda`
parameters can also destructure their arguments using the
`(pattern ...)` syntax. See [Destructuring
parameters](#destructuring-parameters) for examples.
## Calling Functions
### Positional arguments
```{arl}
(define add (lambda (a b) (+ a b)))
(add 3 4)
```
```{arl, include=FALSE}
(assert-equal 7 (add 3 4))
```
### Keyword arguments
Keywords (`:name value`) pass named arguments. This is especially useful
when calling R functions:
```{arl}
(seq :from 1 :to 5)
```
```{arl, include=FALSE}
(assert-equal (c 1 2 3 4 5) (seq :from 1 :to 5))
```
Keywords also work with Arl-defined functions — the keyword name is
matched to the parameter name:
```{arl}
(define make-point
(lambda (x y)
(list x y)))
(make-point :y 20 :x 10)
```
```{arl, include=FALSE}
(assert-equal (list 10 20) (make-point :y 20 :x 10))
```
See [R Interop](r-interop.html#keyword-syntax) for details on keyword
syntax and quoting.
## Local Functions
Use `let`, `let*`, and `letrec` to bind functions in local scope.
### `let` / `let*` for simple local functions
```{arl}
(let ((double (lambda (x) (* x 2)))
(inc (lambda (x) (+ x 1))))
(double (inc 3)))
```
```{arl, include=FALSE}
(assert-equal 8 (let ((double (lambda (x) (* x 2)))
(inc (lambda (x) (+ x 1))))
(double (inc 3))))
```
### `letrec` for recursive local functions
`letrec` allows bindings to refer to each other, which is necessary for
local recursive or mutually-recursive functions:
```{arl}
(letrec ((even? (lambda (n)
(if (= n 0) #t (odd? (- n 1)))))
(odd? (lambda (n)
(if (= n 0) #f (even? (- n 1))))))
(list (even? 10) (odd? 7)))
```
```{arl, include=FALSE}
(assert-equal (list #t #t)
(letrec ((even? (lambda (n) (if (= n 0) #t (odd? (- n 1)))))
(odd? (lambda (n) (if (= n 0) #f (even? (- n 1))))))
(list (even? 10) (odd? 7))))
```
Because `letrec` expands into `set!`, self-recursive `letrec` lambdas are
[automatically tail-call optimized](tail-call-optimization.html). (Note that
mutually recursive functions are not!)
## Higher-Order Functions
Arl's standard library provides the usual higher-order toolkit:
```{arl}
(map (lambda (x) (* x x)) (list 1 2 3 4))
(filter even? (list 1 2 3 4 5 6))
```
```{arl, include=FALSE}
(assert-equal (list 1 4 9 16) (map (lambda (x) (* x x)) (list 1 2 3 4)))
(assert-equal (list 2 4 6) (filter even? (list 1 2 3 4 5 6)))
```
```{arl}
(define add5 (partial + 5))
(add5 10)
(define abs-then-double
(compose (lambda (x) (* x 2)) abs))
(abs-then-double -3)
```
```{arl, include=FALSE}
(assert-equal 15 (add5 10))
(assert-equal 6 (abs-then-double -3))
```
See [Standard Library: Higher-Order Functions](lang-functional.html)
for the full reference including `reduce`, `curry`, `juxt`, `memoize`,
and more.
## Recursion
### Self-TCO (automatic)
When you define a named function that calls itself in tail position, the
compiler automatically rewrites it as a loop — no stack overflow:
```{arl}
(define factorial
(lambda (n acc)
(if (< n 2)
acc
(factorial (- n 1) (* acc n)))))
;; No stack overflow (but 100000! is too large
;; to be representable and overflows to Inf)
(factorial 100000 1)
```
```{arl, include=FALSE}
(assert-equal 120 (factorial 5 1))
```
### `loop` / `recur`
For explicit looping or patterns where self-TCO does not apply, use
`loop`/`recur`:
```{arl}
(import looping :refer (loop recur))
(loop ((i 5) (acc 1))
(if (< i 2)
acc
(recur (- i 1) (* acc i))))
```
```{arl, include=FALSE}
(assert-equal 120 (loop ((i 5) (acc 1))
(if (< i 2) acc (recur (- i 1) (* acc i)))))
```
See [Tail Call Optimization](tail-call-optimization.html) for details on
what counts as tail position and how the optimization works.
## Macros vs Functions
Macros use the same parameter syntax (required, optional, rest,
destructuring) but operate on **unevaluated syntax** at compile time
rather than on runtime values. To define a macro, use `defmacro` instead of
`define` and `lambda`:
```{arl, eval=FALSE}
;; Function: runs at eval time, receives evaluated args, returns new value
(define double (lambda (x) (* x 2)))
;; Macro: runs at compile time, receives unevaluated syntax, returns new syntax
(defmacro when (test . body)
`(if ,test (begin ,@body) #nil))
```
See [Macros and Quasiquote](macros.html) for the full guide.
## Related guides
- [Getting Started](getting-started.html)
- [Macros and Quasiquote](macros.html)
- [Tail Call Optimization](tail-call-optimization.html)
- [Standard Library: Higher-Order Functions](lang-functional.html)
- [R Interop and Data Workflows](r-interop.html)
- [Arl Compared to Scheme](arl-vs-scheme.html)