tests/testthat/test-stdlib-dict.R

# Dict operation tests

engine <- make_engine()

# ============================================================================
# Basic dict operations (from collections)
# ============================================================================

thin <- make_cran_thinner()

test_that("basic dict operations work", {
  thin()
  env <- new.env()
  toplevel_env(engine, env = env)

  dict <- get("dict", envir = env)(a = 1, b = 2)
  expect_equal(get("dict-get", envir = env)(dict, "a"), 1)
  expect_equal(get("dict-get", envir = env)(dict, "missing", 99), 99)
  expect_true(get("dict-has?", envir = env)(dict, "b"))
  expect_false(get("dict-has?", envir = env)(dict, "c"))
  expect_equal(get("dict-keys", envir = env)(dict), list("a", "b"))
  expect_equal(get("dict-values", envir = env)(dict), list(1, 2))

  updated <- get("dict-set", envir = env)(dict, "c", 3)
  expect_equal(get("dict-get", envir = env)(updated, "c"), 3)
  removed <- get("dict-remove", envir = env)(updated, "a")
  expect_false(get("dict-has?", envir = env)(removed, "a"))

  merged <- get("dict-merge", envir = env)(get("dict", envir = env)(a = 1, b = 2), get("dict", envir = env)(b = 3, c = 4))
  expect_equal(get("dict-get", envir = env)(merged, "a"), 1)
  expect_equal(get("dict-get", envir = env)(merged, "b"), 3)
  expect_equal(get("dict-get", envir = env)(merged, "c"), 4)
})

# ============================================================================
# dict-update
# ============================================================================

test_that("dict-update applies function to existing value", {
  thin()
  env <- new.env()
  toplevel_env(engine, env = env)
  import_stdlib_modules(engine, c("dict", "math"), env = env)

  result <- engine$eval(engine$read("
    (begin
      (define d (dict :x 1 :y 2))
      (dict-update d \"x\" inc)
      (dict-get d \"x\"))
  ")[[1]], env = env)
  expect_equal(result, 2)
})

test_that("dict-update uses default for missing key", {
  thin()
  env <- new.env()
  toplevel_env(engine, env = env)
  import_stdlib_modules(engine, c("dict", "math"), env = env)

  result <- engine$eval(engine$read("
    (begin
      (define d (dict :x 1))
      (dict-update d \"z\" inc 0)
      (dict-get d \"z\"))
  ")[[1]], env = env)
  expect_equal(result, 1)  # inc(0) = 1
})

test_that("dict-update returns the dict", {
  thin()
  env <- new.env()
  toplevel_env(engine, env = env)
  import_stdlib_modules(engine, c("dict"), env = env)

  result <- engine$eval(engine$read("
    (dict? (dict-update (dict :x 1) \"x\" (lambda (v) (* v 10))))
  ")[[1]], env = env)
  expect_true(result)
})

# ============================================================================
# dict-map
# ============================================================================

test_that("dict-map transforms values", {
  thin()
  env <- new.env()
  toplevel_env(engine, env = env)
  import_stdlib_modules(engine, c("dict"), env = env)

  result <- engine$eval(engine$read("
    (begin
      (define d (dict :x 1 :y 2 :z 3))
      (define d2 (dict-map (lambda (k v) (* v 10)) d))
      (list (dict-get d2 \"x\") (dict-get d2 \"y\") (dict-get d2 \"z\")))
  ")[[1]], env = env)
  expect_equal(result, list(10, 20, 30))
})

test_that("dict-map returns new dict", {
  thin()
  env <- new.env()
  toplevel_env(engine, env = env)
  import_stdlib_modules(engine, c("dict"), env = env)

  result <- engine$eval(engine$read("
    (begin
      (define d (dict :x 1))
      (define d2 (dict-map (lambda (k v) (* v 10)) d))
      ;; Original unchanged
      (dict-get d \"x\"))
  ")[[1]], env = env)
  expect_equal(result, 1)
})

test_that("dict-map on empty dict", {
  thin()
  env <- new.env()
  toplevel_env(engine, env = env)
  import_stdlib_modules(engine, c("dict"), env = env)

  result <- engine$eval(engine$read("
    (begin
      (define d2 (dict-map (lambda (k v) v) (dict)))
      (dict-keys d2))
  ")[[1]], env = env)
  expect_equal(result, list())
})

# ============================================================================
# dict-filter
# ============================================================================

test_that("dict-filter keeps matching entries", {
  thin()
  env <- new.env()
  toplevel_env(engine, env = env)
  import_stdlib_modules(engine, c("dict"), env = env)

  result <- engine$eval(engine$read("
    (begin
      (define d (dict :x 1 :y 2 :z 3))
      (define d2 (dict-filter (lambda (k v) (> v 1)) d))
      (dict-keys d2))
  ")[[1]], env = env)
  expect_equal(sort(unlist(result)), c("y", "z"))
})

test_that("dict-filter returns new dict", {
  thin()
  env <- new.env()
  toplevel_env(engine, env = env)
  import_stdlib_modules(engine, c("dict"), env = env)

  result <- engine$eval(engine$read("
    (dict? (dict-filter (lambda (k v) #t) (dict :x 1)))
  ")[[1]], env = env)
  expect_true(result)
})

# ============================================================================
# dict-for-each
# ============================================================================

test_that("dict-for-each iterates for side effects", {
  thin()
  env <- new.env()
  toplevel_env(engine, env = env)
  import_stdlib_modules(engine, c("dict"), env = env)

  result <- engine$eval(engine$read("
    (begin
      (define acc (list))
      (dict-for-each (lambda (k v) (set! acc (c acc (list v)))) (dict :x 1 :y 2))
      acc)
  ")[[1]], env = env)
  expect_equal(sort(unlist(result)), c(1, 2))
})

test_that("dict-for-each returns nil", {
  thin()
  env <- new.env()
  toplevel_env(engine, env = env)
  import_stdlib_modules(engine, c("dict"), env = env)

  result <- engine$eval(engine$read("
    (dict-for-each (lambda (k v) v) (dict :x 1))
  ")[[1]], env = env)
  expect_null(result)
})

# ============================================================================
# dict->alist / alist->dict
# ============================================================================

test_that("dict->alist converts dict to association list", {
  thin()
  env <- new.env()
  toplevel_env(engine, env = env)
  import_stdlib_modules(engine, c("dict"), env = env)

  result <- engine$eval(engine$read("
    (dict->alist (dict :x 1 :y 2))
  ")[[1]], env = env)

  # Each element should be a (key value) pair
  expect_equal(length(result), 2)
  # Check that we have the right pairs (order should match insertion)
  expect_equal(result[[1]], list("x", 1))
  expect_equal(result[[2]], list("y", 2))
})

test_that("alist->dict converts association list to dict", {
  thin()
  env <- new.env()
  toplevel_env(engine, env = env)
  import_stdlib_modules(engine, c("dict"), env = env)

  result <- engine$eval(engine$read("
    (begin
      (define d (alist->dict (list (list \"x\" 1) (list \"y\" 2))))
      (list (dict-get d \"x\") (dict-get d \"y\")))
  ")[[1]], env = env)
  expect_equal(result, list(1, 2))
})

test_that("dict->alist round-trips with alist->dict", {
  thin()
  env <- new.env()
  toplevel_env(engine, env = env)
  import_stdlib_modules(engine, c("dict"), env = env)

  result <- engine$eval(engine$read("
    (begin
      (define d (dict :a 10 :b 20))
      (define d2 (alist->dict (dict->alist d)))
      (list (dict-get d2 \"a\") (dict-get d2 \"b\")))
  ")[[1]], env = env)
  expect_equal(result, list(10, 20))
})

test_that("dict->alist on empty dict", {
  thin()
  env <- new.env()
  toplevel_env(engine, env = env)
  import_stdlib_modules(engine, c("dict"), env = env)

  result <- engine$eval(engine$read("(dict->alist (dict))")[[1]], env = env)
  expect_equal(result, list())
})

Try the arl package in your browser

Any scripts or data that you put into this service are public.

arl documentation built on March 19, 2026, 5:09 p.m.