---
title: "Standard Library: Types, Equality, and Conversions"
output: arl::arl_html_vignette
pkgdown:
  as_is: true
vignette: >
  %\VignetteIndexEntry{Standard Library: Types, Equality, and Conversions}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

<!-- AUTO-GENERATED from inst/arl/ source files. Do not edit manually. -->
<!-- Regenerate with: make lang-docs -->

```{r setup, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
arl::register_knitr_engine()
```

*Examples on this page may reference functions from other stdlib modules
without showing explicit `(import ...)` statements. In the REPL or your
own code, you will need to import non-prelude modules before using their
exports — see [Importing modules](lang-reference.html#importing-modules).*


## List and Pair Predicates {#section-list-and-pair-predicates}

### list? {#list-p}

Return #t if x is a proper list (R list or call), not a dotted pair.

**Signature:** `(list? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(list? (list 1 2 3))  ; => #t
(list? ())  ; => #t
(list? "hello")  ; => #f
```

```{arl, include=FALSE}
(assert-true (list? (list 1 2 3)))
(assert-true (list? ()))
(assert-false (list? "hello"))
```

**See also:** [pair?](#pair-p), [list-or-pair?](#list-or-pair-p), [null?](#null-p)

---

### list-or-pair? {#list-or-pair-p}

Return #t if x is a non-empty list or dotted pair (pairlist cell).

**Signature:** `(list-or-pair? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(list-or-pair? (list 1))  ; => #t
```

```{arl, include=FALSE}
(assert-true (list-or-pair? (list 1)))
```

**See also:** [list?](#list-p), [pair?](#pair-p), [null?](#null-p), [atom?](#atom-p)

---

### null? {#null-p}

Return #t for empty list or #nil.

**Signature:** `(null? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(null? #nil)  ; => #t
(null? ())  ; => #t
(null? (list 1))  ; => #f
```

```{arl, include=FALSE}
(assert-true (null? #nil))
(assert-true (null? ()))
(assert-false (null? (list 1)))
```

**See also:** [nil?](#nil-p), [pair?](#pair-p), [list-or-pair?](#list-or-pair-p)

---

### nil? {#nil-p}

Alias for null?.

**Signature:** `(nil? x)`

**Parameters:**
- **`x`** — Value to test

**See also:** [null?](#null-p)

---

### atom? {#atom-p}

Return #t if x is not a non-empty list or
dotted pair (i.e. not list-or-pair?).

**Signature:** `(atom? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(atom? 42)  ; => #t
(atom? "hello")  ; => #t
(atom? (list 1 2))  ; => #f
(atom? ())  ; => #t  (empty list is atomic)
```

```{arl, include=FALSE}
(assert-true (atom? 42))
(assert-true (atom? "hello"))
(assert-false (atom? (list 1 2)))
(assert-true (atom? ()))
```

**See also:** [list-or-pair?](#list-or-pair-p)

---

### empty? {#empty-p}

Return #t if x is empty (0-length).
(Note the empty string "" has length 1 and is not "empty"!)

**Signature:** `(empty? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(empty? ())  ; => #t
(empty? (list))  ; => #t
(empty? (list 1))  ; => #f
```

```{arl, include=FALSE}
(assert-true (empty? ()))
(assert-true (empty? (list)))
(assert-false (empty? (list 1)))
```

**Note:** The empty string `""` has length 1 in R and is NOT considered empty!

**See also:** [null?](#null-p), [length=](lang-list-seq.html#length-eq) (in `sequences` module)

---

### pair? {#pair-p}

Return #t if x is a cons cell (dotted pair).

**Signature:** `(pair? x)`

**Examples:**
```{arl}
(pair? (cons 1 2))        ; => #t (dotted pair)
(pair? (cons 1 (list 2 3)))  ; => #f (cons onto list returns a list)
(pair? '(1 2 3))          ; => #f (quoted list, not a cons cell)
(pair? '())               ; => #f
```

```{arl, include=FALSE}
(assert-true (pair? (cons 1 2)))
(assert-false (pair? (cons 1 (list 2 3))))
(assert-false (pair? '(1 2 3)))
(assert-false (pair? '()))
```

**See also:** [list?](#list-p), [null?](#null-p), [atom?](#atom-p)

---

## Symbol Predicates {#section-symbol-predicates}

### symbol? {#symbol-p}

Return #t if x is a symbol.

**Signature:** `(symbol? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(symbol? 'foo)  ; => #t
(symbol? "foo")  ; => #f
(symbol? 42)  ; => #f
```

```{arl, include=FALSE}
(assert-true (symbol? 'foo))
(assert-false (symbol? "foo"))
(assert-false (symbol? 42))
```

---

### keyword? {#keyword-p}

Return #t if x is an Arl keyword.

**Signature:** `(keyword? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(keyword? ':foo)  ; => #t
(keyword? 'foo)   ; => #f
```

```{arl, include=FALSE}
(assert-true (keyword? ':foo))
(assert-false (keyword? 'foo))
```

---

## Basic Type Predicates {#section-basic-type-predicates}

### number? {#number-p}

Return #t if x is a number (real or complex).

**Signature:** `(number? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(number? 42)  ; => #t
```

```{arl, include=FALSE}
(assert-true (number? 42))
```

**See also:** [real?](#real-p), [complex?](#complex-p), [rational?](#rational-p), [exact?](#exact-p), [inexact?](#inexact-p)

---

### string? {#string-p}

Return #t if x is character.

**Signature:** `(string? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(string? "hello")  ; => #t
```

```{arl, include=FALSE}
(assert-true (string? "hello"))
```

---

### vector? {#vector-p}

Return #t if x is a non-list atomic vector.

**Signature:** `(vector? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(vector? (c 1 2 3))  ; => #t
```

```{arl, include=FALSE}
(assert-true (vector? (c 1 2 3)))
```

**Note:** Tests whether x is a non-list atomic vector (numeric, character, logical, etc.). R lists are NOT vectors by this predicate.

---

### boolean? {#boolean-p}

Return #t if x is a single logical value.

**Signature:** `(boolean? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(boolean? #t)  ; => #t
(boolean? #f)  ; => #t
(boolean? TRUE)  ; => #t
(boolean? (c TRUE FALSE))  ; => #f
```

```{arl, include=FALSE}
(assert-true (boolean? #t))
(assert-true (boolean? #f))
(assert-true (boolean? TRUE))
(assert-false (boolean? (c TRUE FALSE)))
```

---

### true? {#true-p}

Return #t if x is TRUE.

**Signature:** `(true? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(true? #t)  ; => #t
```

```{arl, include=FALSE}
(assert-true (true? #t))
```

---

### false? {#false-p}

Return #t if x is #f.

**Signature:** `(false? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(false? #f)  ; => #t
```

```{arl, include=FALSE}
(assert-true (false? #f))
```

---

## Function Predicates {#section-function-predicates}

### fn? {#fn-p}

Return #t if x is a function.

**Signature:** `(fn? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(fn? car)  ; => #t
```

```{arl, include=FALSE}
(assert-true (fn? car))
```

**See also:** [callable?](#callable-p), [procedure?](#procedure-p)

---

### callable? {#callable-p}

Alias for fn?.

**Signature:** `(callable? x)`

**Parameters:**
- **`x`** — Value to test

**See also:** [fn?](#fn-p)

---

### procedure? {#procedure-p}

Alias for fn? - return #t if x is a function.

**Signature:** `(procedure? x)`

**Parameters:**
- **`x`** — Value to test

**See also:** [fn?](#fn-p), [callable?](#callable-p)

---

## Environment Predicates {#section-environment-predicates}

### environment? {#environment-p}

Test if x is an environment (including R6 objects).

**Signature:** `(environment? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(environment? (baseenv))  ; => #t
```

```{arl, include=FALSE}
(assert-true (environment? (baseenv)))
```

**Note:** R6 objects, dicts, and sets are all environments in R, so this predicate returns #t for them.

---

### is-refclass? {#is-refclass-p}

Test if x is a Reference Class object.

**Signature:** `(is-refclass? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(is-refclass? (baseenv))  ; => #f
```

```{arl, include=FALSE}
(assert-false (is-refclass? (baseenv)))
```

---

## Type Introspection {#section-type-introspection}

### type-of {#type-of}

Return the type of value (alias for R's typeof).

**Signature:** `(type-of x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(type-of 42)  ; => "double"
```

```{arl, include=FALSE}
(assert-equal "double" (type-of 42))
```

**See also:** class, mode (R functions)

---

## Numeric Type Predicates {#section-numeric-type-predicates}

Arl implements a numeric tower adapted for R's type system:

```
number?    (is.numeric OR is.complex)
├─ complex?  (is.complex)
└─ real?     (is.numeric AND NOT is.complex)
   ├─ ±Inf   (real but not rational)
   └─ rational? (real? AND is.finite)
      └─ integer? (is.finite AND is.numeric AND x == as.integer(x))
         └─ natural? (integer? AND x >= 0)

Orthogonal predicates:
exact?   (is.integer - storage type)
inexact? (number? AND NOT is.integer)
```

### real? {#real-p}

Return #t if x is a real number (includes ±Inf, excludes complex).

**Signature:** `(real? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(real? 42)            ; => #t
(real? 3.14)          ; => #t
(real? Inf)           ; => #t
(real? (make-rectangular 3 4))  ; => #f
```

```{arl, include=FALSE}
(assert-true (real? 42))
(assert-true (real? 3.14))
(assert-true (real? Inf))
(assert-false (real? (make-rectangular 3 4)))
```

**See also:** [rational?](#rational-p), [complex?](#complex-p), [number?](#number-p)

---

### complex? {#complex-p}

Return #t if x is a complex number.

**Signature:** `(complex? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(complex? (make-rectangular 3 4))  ; => #t
(complex? 42)         ; => #f
```

```{arl, include=FALSE}
(assert-true (complex? (make-rectangular 3 4)))
(assert-false (complex? 42))
```

**See also:** [real?](#real-p), [number?](#number-p), [make-rectangular](lang-math.html#make-rectangular) (in `math` module)

---

### rational? {#rational-p}

Return #t if x is a finite real number (rational in R are finite floats).

**Signature:** `(rational? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(rational? 42)        ; => #t
(rational? 3.14)      ; => #t
(rational? Inf)       ; => #f (infinities are real but not rational)
(rational? NaN)       ; => #f
```

```{arl, include=FALSE}
(assert-true (rational? 42))
(assert-true (rational? 3.14))
(assert-false (rational? Inf))
(assert-false (rational? NaN))
```

**Note:** In R, all finite floating-point numbers can be represented as rationals (IEEE 754). R does not have a built-in rational or decimal type like some Schemes.

**See also:** [real?](#real-p), [integer?](#integer-p), [number?](#number-p)

---

### exact? {#exact-p}

Return #t if x is an exact number (integer storage type in R).

**Signature:** `(exact? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(exact? 5L)           ; => #t (integer type)
(exact? 5.0)          ; => #f (double type)
(exact? (->integer 5)); => #t
```

```{arl, include=FALSE}
(assert-true (exact? 5L))
(assert-false (exact? 5.0))
(assert-true (exact? (->integer 5)))
```

**Note:** In Scheme, exactness is a property of the number. In R (and Arl), exactness corresponds to integer storage type. All integers are exact; all doubles and complex numbers are inexact.

**See also:** [inexact?](#inexact-p), [exact->inexact](#exact-to-inexact) (in `conversions` module)

---

### inexact? {#inexact-p}

Return #t if x is an inexact number (double or complex in R).

**Signature:** `(inexact? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(inexact? 5.0)        ; => #t
(inexact? (make-rectangular 3 4))  ; => #t
(inexact? 5L)         ; => #f
```

```{arl, include=FALSE}
(assert-true (inexact? 5.0))
(assert-true (inexact? (make-rectangular 3 4)))
(assert-false (inexact? 5L))
```

**See also:** [exact?](#exact-p), [inexact->exact](#inexact-to-exact) (in `conversions` module)

---

### integer? {#integer-p}

Return #t if x is an integer-valued number.

**Signature:** `(integer? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(integer? 42)         ; => #t
(integer? 42.0)       ; => #t (value is integer even if storage is double)
(integer? 3.14)       ; => #f
(integer? Inf)        ; => #f
```

```{arl, include=FALSE}
(assert-true (integer? 42))
(assert-true (integer? 42.0))
(assert-false (integer? 3.14))
(assert-false (integer? Inf))
```

**Note:** Tests for integer VALUE, not storage type. Use `exact?` to test storage type.

**See also:** [natural?](#natural-p), [exact?](#exact-p), [rational?](#rational-p)

---

### natural? {#natural-p}

Return #t if x is a natural number (integer >= 0).

**Signature:** `(natural? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(natural? 0)          ; => #t
(natural? 42)         ; => #t
(natural? -5)         ; => #f
(natural? 3.14)       ; => #f
```

```{arl, include=FALSE}
(assert-true (natural? 0))
(assert-true (natural? 42))
(assert-false (natural? -5))
(assert-false (natural? 3.14))
```

**See also:** [integer?](#integer-p), [positive?](#positive-p), [non-negative?](#non-negative-p)

---

### finite? {#finite-p}

Return #t if x is finite.

**Signature:** `(finite? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(finite? 42)          ; => #t
(finite? Inf)         ; => #f
(finite? NaN)         ; => #f
(finite? -Inf)        ; => #f
```

```{arl, include=FALSE}
(assert-true (finite? 42))
(assert-false (finite? Inf))
(assert-false (finite? NaN))
(assert-false (finite? -Inf))
```

**See also:** [infinite?](#infinite-p), [nan?](#nan-p), [rational?](#rational-p)

---

### infinite? {#infinite-p}

Return #t if x is infinite.

**Signature:** `(infinite? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(infinite? Inf)       ; => #t
(infinite? -Inf)      ; => #t
(infinite? 42)        ; => #f
(infinite? NaN)       ; => #f
```

```{arl, include=FALSE}
(assert-true (infinite? Inf))
(assert-true (infinite? -Inf))
(assert-false (infinite? 42))
(assert-false (infinite? NaN))
```

**See also:** [finite?](#finite-p), [nan?](#nan-p)

---

### nan? {#nan-p}

Return #t if x is NaN.

**Signature:** `(nan? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(nan? NaN)            ; => #t
(nan? (/ 0 0))        ; => #t (0/0 = NaN)
(nan? 42)             ; => #f
(nan? Inf)            ; => #f
```

```{arl, include=FALSE}
(assert-true (nan? NaN))
(assert-true (nan? (/ 0 0)))
(assert-false (nan? 42))
(assert-false (nan? Inf))
```

**See also:** [finite?](#finite-p), [infinite?](#infinite-p)

---

## Value Predicates {#section-value-predicates}

### even? {#even-p}

Return #t if x is an even number.

**Signature:** `(even? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(even? 4)             ; => #t
(even? 3)             ; => #f
(even? 0)             ; => #t
```

```{arl, include=FALSE}
(assert-true (even? 4))
(assert-false (even? 3))
(assert-true (even? 0))
```

**See also:** [odd?](#odd-p)

---

### odd? {#odd-p}

Return #t if x is an odd number.

**Signature:** `(odd? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(odd? 3)              ; => #t
(odd? 4)              ; => #f
```

```{arl, include=FALSE}
(assert-true (odd? 3))
(assert-false (odd? 4))
```

**See also:** [even?](#even-p)

---

### zero? {#zero-p}

Return #t if x is zero.

**Signature:** `(zero? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(zero? 0)             ; => #t
(zero? 0.0)           ; => #t
(zero? 1)             ; => #f
```

```{arl, include=FALSE}
(assert-true (zero? 0))
(assert-true (zero? 0.0))
(assert-false (zero? 1))
```

**See also:** [positive?](#positive-p), [negative?](#negative-p)

---

### positive? {#positive-p}

Return #t if x is greater than zero.

**Signature:** `(positive? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(positive? 5)         ; => #t
(positive? 0)         ; => #f
(positive? -5)        ; => #f
```

```{arl, include=FALSE}
(assert-true (positive? 5))
(assert-false (positive? 0))
(assert-false (positive? -5))
```

**See also:** [negative?](#negative-p), [non-negative?](#non-negative-p), [zero?](#zero-p)

---

### negative? {#negative-p}

Return #t if x is less than zero.

**Signature:** `(negative? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(negative? -5)        ; => #t
(negative? 0)         ; => #f
(negative? 5)         ; => #f
```

```{arl, include=FALSE}
(assert-true (negative? -5))
(assert-false (negative? 0))
(assert-false (negative? 5))
```

**See also:** [positive?](#positive-p), [non-positive?](#non-positive-p), [zero?](#zero-p)

---

### non-negative? {#non-negative-p}

Return #t if x is greater than or equal to zero.

**Signature:** `(non-negative? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(non-negative? 5)     ; => #t
(non-negative? 0)     ; => #t
(non-negative? -5)    ; => #f
```

```{arl, include=FALSE}
(assert-true (non-negative? 5))
(assert-true (non-negative? 0))
(assert-false (non-negative? -5))
```

**See also:** [positive?](#positive-p), [natural?](#natural-p)

---

### non-positive? {#non-positive-p}

Return #t if x is less than or equal to zero.

**Signature:** `(non-positive? x)`

**Parameters:**
- **`x`** — Value to test

**Examples:**
```{arl}
(non-positive? -5)    ; => #t
(non-positive? 0)     ; => #t
(non-positive? 5)     ; => #f
```

```{arl, include=FALSE}
(assert-true (non-positive? -5))
(assert-true (non-positive? 0))
(assert-false (non-positive? 5))
```

**See also:** [negative?](#negative-p)

---

## Equality Predicates {#section-equality-predicates}

Arl provides `equal?` for deep structural equality with S3-style dispatch,
and `identical?` for R's native identity comparison. The Scheme predicates
`eq?` and `eqv?` are intentionally not implemented because R does not
provide the pointer-level semantics they require.

### equal? {#equal-p}

Deep structural equality. Dispatches on class of
first argument. Optional :strict #t uses identical? for atomics.
Add methods with (set-method! 'equal? 'my-class (lambda (a b strict) ...)).

**Signature:** `(equal? a b [strict #f])`

**Parameters:**
- **`a`** — First value
- **`b`** — Second value
- **`strict`** — When #t, use identical? for atomics (default #f)

**Examples:**
```{arl}
(equal? 1 1)                    ; => #t
(equal? 1 1.0)                  ; => #t (type coercion)
(equal? 1L 1.0 :strict #t)     ; => #f (strict mode, uses identical?)
(equal? (list 1 2) (list 1 2))  ; => #t (deep structural)
(equal? "hello" "hello")        ; => #t
```

```{arl, include=FALSE}
(assert-true (equal? 1 1))
(assert-true (equal? 1 1.0))
(assert-false (equal? 1L 1.0 :strict #t))
(assert-true (equal? (list 1 2) (list 1 2)))
(assert-true (equal? "hello" "hello"))
```

**See also:** [identical?](#identical-p), [eq?](#eq-p), [eqv?](#eqv-p), [set-method!](#set-method-bang)

---

### identical? {#identical-p}

R's native equality test. Structural comparison for value
  types, pointer comparison for reference types.

**Signature:** `(identical? a b)`

**Parameters:**
- **`a`** — First value
- **`b`** — Second value

**Examples:**
```{arl}
(identical? 1 1)          ; => #t
(identical? 1L 1.0)       ; => #f (integer vs double)
(identical? "a" "a")      ; => #t
```

```{arl, include=FALSE}
(assert-true (identical? 1 1))
(assert-false (identical? 1L 1.0))
(assert-true (identical? "a" "a"))
```

**Note:** This is R's `identical()` with no modifications. Use `equal?`
  for deep structural equality with type coercion.

**See also:** [equal?](#equal-p), [eq?](#eq-p), [eqv?](#eqv-p)

---

### eq? {#eq-p}

**Not implemented in Arl.** Raises an error when called.

**Signature:** `(eq? a b)`

**Parameters:**
- **`a`** — First value
- **`b`** — Second value

**Note:** True Scheme `eq?` semantics cannot be properly implemented in R because R does not provide reliable pointer equality for all object types. R's `identical()` does structural comparison for some types (lists, vectors) but pointer comparison for others (environments, reference classes). Use `identical?` for R's native equality, or `equal?` for deep structural equality.

**See also:** [identical?](#identical-p), [equal?](#equal-p)

---

### eqv? {#eqv-p}

**Not implemented in Arl.** Raises an error when called.

**Signature:** `(eqv? a b)`

**Parameters:**
- **`a`** — First value
- **`b`** — Second value

**Note:** True Scheme `eqv?` semantics cannot be properly implemented in R because R does not provide reliable pointer equality for all object types. Use `identical?` for R's native equality, or `equal?` for deep structural equality.

**See also:** [identical?](#identical-p), [equal?](#equal-p)

---

## Helper Functions for Equal? {#section-helper-functions-for-equal}

Internal helper functions used by the `equal?` dispatch methods to perform
recursive structural comparison of environments and lists.

### env-equal? {#env-equal-p}

Compare two environments by their bindings and values.

**Signature:** `(env-equal? env1 env2)`

**Parameters:**
- **`env1`** — First environment
- **`env2`** — Second environment

**Examples:**
```{arl}
(define e1 (new.env :parent (emptyenv)))
(define e2 (new.env :parent (emptyenv)))
(assign "x" 1 :envir e1)
(assign "x" 1 :envir e2)
(env-equal? e1 e2)              ; => #t
```

**Note:** Used internally by `equal?.environment`. Compares environments by sorting their bindings and recursively comparing values.

**See also:** [equal?.environment](#equal-p-environment)

---

### list-equal? {#list-equal-p}

Recursively compare list elements.

**Signature:** `(list-equal? lst1 lst2)`

**Parameters:**
- **`lst1`** — First list
- **`lst2`** — Second list

**Examples:**
```{arl}
(list-equal? (list 1 2 3) (list 1 2 3))  ; => #t
(list-equal? (list 1 2) (list 1 2 3))     ; => #f
(list-equal? (list 1 "a") (list 1 "a"))   ; => #t
```

```{arl, include=FALSE}
(assert-true (list-equal? (list 1 2 3) (list 1 2 3)))
(assert-false (list-equal? (list 1 2) (list 1 2 3)))
(assert-true (list-equal? (list 1 "a") (list 1 "a")))
```

**Note:** Used internally by `equal?.list`. Recursively compares list elements using `equal?`.

**See also:** [equal?.list](#equal-p-list)

---

## S3 Dispatch System {#section-s3-dispatch-system}

Arl implements a simplified S3-style dispatch system for generic functions
like `equal?`. Types are identified by their first S3 class, and methods
are registered as `generic.class` bindings in the top-level environment.

### s3-type {#s3-type}

Extract the first S3 class from an object.

**Signature:** `(s3-type obj)`

**Parameters:**
- **`obj`** — Object to get the S3 class of

**Examples:**
```{arl}
(s3-type 42)                    ; => "numeric"
(s3-type "hello")               ; => "character"
(s3-type (list 1 2))            ; => "list"
(s3-type (new.env))             ; => "environment"
```

```{arl, include=FALSE}
(assert-equal "numeric" (s3-type 42))
(assert-equal "character" (s3-type "hello"))
(assert-equal "list" (s3-type (list 1 2)))
(assert-equal "environment" (s3-type (new.env)))
```

---

### check-s3-type-match {#check-s3-type-match}

Check if all objects have the same S3 type.

**Signature:** `(check-s3-type-match obj rest...)`

**Parameters:**
- **`obj`** — First object
- **`rest`** — Additional objects to compare against

**Examples:**
```{arl}
(check-s3-type-match 1 2 3)     ; => #t (all numeric)
(check-s3-type-match 1 "a")     ; => #f (numeric vs character)
```

```{arl, include=FALSE}
(assert-true (check-s3-type-match 1 2 3))
(assert-false (check-s3-type-match 1 "a"))
```

**Note:** Internal function used by `equal?` to verify type consistency before dispatch.

---

### set-method! {#set-method-bang}

Register an S3-style method.
Example: (set-method! 'equal? 'my-class (lambda (a b strict) ...)).

**Signature:** `(set-method! generic-name class-name method-fun)`

**Parameters:**
- **`generic-name`** — Symbol naming the generic function (e.g. 'equal?)
- **`class-name`** — Symbol naming the S3 class to dispatch on
- **`method-fun`** — Implementation function for this class

**Examples:**
```{arl}
;; Register a custom equality method for "point" objects:
(set-method! 'equal? 'point
  (lambda (a b strict)
    (and (equal? ($ a "x") ($ b "x"))
         (equal? ($ a "y") ($ b "y")))))
```

```{arl, include=FALSE}
(set-method! 'equal? 'test-dummy (lambda (a b strict) #t))
(assert-true (is.function (get "equal?.test-dummy" :envir (toplevel-env))))
```

**See also:** [use-method](#use-method)

---

### use-method {#use-method}

Dispatch to an S3 method based on object class.

**Signature:** `(use-method generic-name obj args)`

**Parameters:**
- **`generic-name`** — String naming the generic (e.g. "equal?")
- **`obj`** — Object whose class determines which method to call
- **`args`** — List of arguments to pass to the method

**Examples:**
```{arl}
(use-method "equal?" (list 1 2) (list (list 1 2) (list 1 2) #f))  ; dispatches to equal?.list
```

```{arl, include=FALSE}
(assert-true (use-method "equal?" (list 1 2) (list (list 1 2) (list 1 2) #f)))
```

**Note:** Internal function. Dispatches to the appropriate S3 method based on the object's class, falling back to the `.default` method if no class-specific method is found.

**See also:** [set-method!](#set-method-bang), [s3-type](#s3-type)

---

## Built-in Equal? Methods {#section-built-in-equal-methods}

These methods handle equality comparison for R's core types. The dispatch
system selects the appropriate method based on the S3 class of the first
argument. Users can register additional methods with `set-method!`.

### equal?.default {#equal-p-default}

Default equality: atomic/vector comparison.
:strict #t => #f; else use == with type coercion.

**Signature:** `(equal?.default a b [strict #f])`

**Parameters:**
- **`a`** — First value
- **`b`** — Second value
- **`strict`** — When #t, use identical? instead of == (default #f)

**Examples:**
```{arl}
(equal?.default 1 1)             ; => #t
(equal?.default 1 1.0)           ; => #t (coercion via ==)
(equal?.default 1L 1.0 :strict #t) ; => #f (strict uses identical?)
(equal?.default (c 1 2 3) (c 1 2 3))  ; => #t (element-wise)
```

```{arl, include=FALSE}
(assert-true (equal?.default 1 1))
(assert-true (equal?.default 1 1.0))
(assert-false (equal?.default 1L 1.0 :strict #t))
```

---

### equal?.list {#equal-p-list}

Compare lists recursively by structure and elements.

**Signature:** `(equal?.list a b [strict #f])`

**Parameters:**
- **`a`** — First list
- **`b`** — Second list
- **`strict`** — When #t, use strict comparison (default #f)

**Examples:**
```{arl}
(equal?.list (list 1 2 3) (list 1 2 3))          ; => #t
(equal?.list (list 1 (list 2 3)) (list 1 (list 2 3)))  ; => #t (nested)
(equal?.list (list 1 2) (list 1 2 3))             ; => #f
```

```{arl, include=FALSE}
(assert-true (equal?.list (list 1 2 3) (list 1 2 3)))
(assert-false (equal?.list (list 1 2) (list 1 2 3)))
(assert-true (equal?.list (list 1 (list 2 3)) (list 1 (list 2 3))))
```

---

### equal?.environment {#equal-p-environment}

Compare environments by bindings and values
(dict, set, R6, refclass are env-based).

**Signature:** `(equal?.environment a b [strict #f])`

**Parameters:**
- **`a`** — First environment
- **`b`** — Second environment
- **`strict`** — When #t, use strict comparison (default #f)

**Examples:**
```{arl}
(define e1 (new.env :parent (emptyenv)))
(define e2 (new.env :parent (emptyenv)))
(assign "x" 1 :envir e1)
(assign "x" 1 :envir e2)
(equal?.environment e1 e2)       ; => #t
```

```{arl, include=FALSE}
(define _e1 (new.env :parent (emptyenv)))
(define _e2 (new.env :parent (emptyenv)))
(assign "x" 1 :envir _e1)
(assign "x" 1 :envir _e2)
(assert-true (equal?.environment _e1 _e2))
```

---

## Symbol Conversions {#section-symbol-conversions}

### symbol->string {#symbol-to-string}

Convert symbol to string.

**Signature:** `(symbol->string sym)`

**Parameters:**
- **`sym`** — Symbol to convert to string

**Examples:**
```{arl}
(symbol->string 'hello)  ; => "hello"
(symbol->string 'x)      ; => "x"
```

```{arl, include=FALSE}
(assert-equal "hello" (symbol->string 'hello))
(assert-equal "x" (symbol->string 'x))
```

**Note:** Signals an error if the argument is not a symbol.

**See also:** [string->symbol](#string-to-symbol), [->symbol](#to-symbol)

---

### string->symbol {#string-to-symbol}

Convert string to symbol.

**Signature:** `(string->symbol str)`

**Parameters:**
- **`str`** — String to convert to symbol

**Examples:**
```{arl}
(string->symbol "hello")  ; => hello (a symbol)
(string->symbol "x")      ; => x
```

```{arl, include=FALSE}
(assert-true (symbol? (string->symbol "hello")))
(assert-equal 'hello (string->symbol "hello"))
```

**Note:** Signals an error if the argument is not a string.

**See also:** [symbol->string](#symbol-to-string), [->symbol](#to-symbol)

---

### ->symbol {#to-symbol}

Convert value to symbol.

**Signature:** `(->symbol x)`

**Parameters:**
- **`x`** — Value to convert to symbol

**Examples:**
```{arl}
(->symbol "hello")  ; => hello (a symbol)
(->symbol 42)       ; => 42 (symbol named "42")
(->symbol 'x)       ; => x (already a symbol, returned as-is)
```

```{arl, include=FALSE}
(assert-true (symbol? (->symbol "hello")))
(assert-true (symbol? (->symbol 42)))
(assert-equal 'x (->symbol 'x))
```

**See also:** [symbol->string](#symbol-to-string), [string->symbol](#string-to-symbol)

---

## Numeric Conversions {#section-numeric-conversions}

### ->number {#to-number}

Convert value to number.

**Signature:** `(->number x)`

**Parameters:**
- **`x`** — Value to convert to number

**Examples:**
```{arl}
(->number "42")     ; => 42
(->number "3.14")   ; => 3.14
(->number 5)        ; => 5 (already a number)
```

```{arl, include=FALSE}
(assert-equal 42 (->number "42"))
(assert-equal 3.14 (->number "3.14"))
(assert-equal 5 (->number 5))
```

**Note:** Signals an error for non-numeric strings (e.g. `(->number "abc")`).

**See also:** [->integer](#to-integer), [->double](#to-double), [->complex](#to-complex)

---

## Scheme-Style Exact/Inexact Conversions {#section-scheme-style-exact-inexact-conversions}

### exact->inexact {#exact-to-inexact}

Convert exact number to inexact (integer to double).

**Signature:** `(exact->inexact x)`

**Parameters:**
- **`x`** — Exact (integer) number to convert to double

**Examples:**
```{arl}
(exact->inexact 5L)     ; => 5.0 (integer to double)
(exact->inexact 3.14)   ; => 3.14 (already double, returned as double)
```

```{arl, include=FALSE}
(assert-equal 5.0 (exact->inexact 5L))
(assert-equal 3.14 (exact->inexact 3.14))
```

**Note:** In Scheme, exactness is a property of any number. In R (and Arl), exact means integer storage type and inexact means double or complex. See also `exact?` and `inexact?` in the `math` module.

**See also:** [inexact->exact](#inexact-to-exact), [exact?](#exact-p) (in `types` module), [inexact?](#inexact-p) (in `types` module)

---

### inexact->exact {#inexact-to-exact}

Convert inexact number to exact (double to integer, rounds to nearest).

**Signature:** `(inexact->exact x)`

**Parameters:**
- **`x`** — Inexact (double) number to round to integer

**Examples:**
```{arl}
(inexact->exact 3.7)    ; => 4L (rounds to nearest integer)
(inexact->exact 3.2)    ; => 3L
(inexact->exact 5L)     ; => 5L (already integer)
```

```{arl, include=FALSE}
(assert-equal 4L (inexact->exact 3.7))
(assert-equal 3L (inexact->exact 3.2))
(assert-equal 5L (inexact->exact 5L))
```

**Note:** **Warning:** Converting double to integer rounds to the nearest integer, which may lose precision. For example, `(inexact->exact 3.7)` returns `4L`, not `3L`.

**See also:** [exact->inexact](#exact-to-inexact), [exact?](#exact-p) (in `types` module), [inexact?](#inexact-p) (in `types` module)

---

## R-Style Numeric Conversions {#section-r-style-numeric-conversions}

R-style type conversions providing explicit control over the target numeric type.

### ->integer {#to-integer}

Convert value to integer.

**Signature:** `(->integer x)`

**Parameters:**
- **`x`** — Value to convert to integer

**Examples:**
```{arl}
(->integer 3.7)     ; => 3L (truncates toward zero)
(->integer "42")    ; => 42L
(->integer 5L)      ; => 5L (already integer)
```

```{arl, include=FALSE}
(assert-equal 3L (->integer 3.7))
(assert-equal 42L (->integer "42"))
(assert-equal 5L (->integer 5L))
```

**Note:** Signals an error for non-numeric strings. Note that `->integer` truncates toward zero (like R's `as.integer`), whereas `inexact->exact` rounds to nearest.

**See also:** [->number](#to-number), [->double](#to-double), [inexact->exact](#inexact-to-exact)

---

### ->double {#to-double}

Convert value to double.

**Signature:** `(->double x)`

**Parameters:**
- **`x`** — Value to convert to double

**Examples:**
```{arl}
(->double 5L)       ; => 5.0
(->double "3.14")   ; => 3.14
(->double 2.5)      ; => 2.5 (already double)
```

```{arl, include=FALSE}
(assert-equal 5.0 (->double 5L))
(assert-equal 3.14 (->double "3.14"))
(assert-equal 2.5 (->double 2.5))
```

**See also:** [->number](#to-number), [->integer](#to-integer), [exact->inexact](#exact-to-inexact)

---

### ->complex {#to-complex}

Convert value to complex number (imaginary part = 0).

**Signature:** `(->complex x)`

**Parameters:**
- **`x`** — Value to convert to complex number

**Examples:**
```{arl}
(->complex 5)       ; => 5+0i
(->complex 3.14)    ; => 3.14+0i
(->complex "2+3i")  ; => 2+3i
```

```{arl, include=FALSE}
(assert-true (complex? (->complex 5)))
(assert-equal (complex :real 5 :imaginary 0) (->complex 5))
```

**See also:** [->number](#to-number), [->double](#to-double), [make-rectangular](lang-math.html#make-rectangular) (in `types` module)

---

## Collection Conversions {#section-collection-conversions}

### ->list {#to-list}

Convert value to list.

**Signature:** `(->list x)`

**Parameters:**
- **`x`** — Value to convert to list

**Examples:**
```{arl}
(->list (c 1 2 3))       ; => (1 2 3)
(->list (list 1 2 3))    ; => (1 2 3) (already a list)
```

```{arl, include=FALSE}
(assert-equal (list 1 2 3) (->list (c 1 2 3)))
(assert-equal (list 1 2 3) (->list (list 1 2 3)))
```

**See also:** [->vector](#to-vector)

---

### ->vector {#to-vector}

Convert value to vector.

**Signature:** `(->vector x)`

**Parameters:**
- **`x`** — Value to convert to atomic vector

**Examples:**
```{arl}
(->vector (list 1 2 3))   ; => [1] 1 2 3 (atomic vector)
(->vector (c 1 2 3))  ; => [1] 1 2 3 (already a vector)
```

```{arl, include=FALSE}
(assert-true (vector? (->vector (list 1 2 3))))
(assert-equal (c 1 2 3) (->vector (list 1 2 3)))
```

**Note:** When given a list, `->vector` flattens it into an atomic vector using `unlist`. Nested lists will be recursively flattened.

**See also:** [->list](#to-list)

---

