---
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)
