automatically create new values in a table

--- a/ac.scm	2009-12-21 10:18:00.000000000 -0500
+++ b/ac.scm	2009-12-21 11:21:21.000000000 -0500
@@ -632,6 +632,19 @@
 ; default vals with them.  To make compatible with existing written tables, 
 ; just use an atom or 3-elt list to keep the default.
+(define table-vivifier (make-hash-table 'weak))
+(define (ar-table-get fn args)
+  (ar-nill
+   (or (hash-table-get fn 
+                       (car args) 
+                       (if (pair? (cdr args)) (cadr args) #f))
+       (let ((vivifier (hash-table-get table-vivifier fn #f)))
+         (and vivifier
+              (let ((val (vivifier)))
+                (hash-table-put! fn (car args) val)
+                val))))))
 (define (ar-apply fn args)
   (cond ((procedure? fn) 
          (apply fn args))
@@ -639,10 +652,7 @@
          (list-ref fn (car args)))
         ((string? fn) 
          (string-ref fn (car args)))
-        ((hash-table? fn) 
-         (ar-nill (hash-table-get fn 
-                                  (car args) 
-                                  (if (pair? (cdr args)) (cadr args) #f))))
+        ((hash-table? fn) (ar-table-get fn args))
 ; experiment: means e.g. [1] is a constant fn
 ;       ((or (number? fn) (symbol? fn)) fn)
 ; another possibility: constant in functional pos means it gets 
@@ -1064,7 +1074,10 @@
 (xdef table (lambda args
               (let ((h (make-hash-table 'equal)))
-                (if (pair? args) ((car args) h))
+                (if (and (pair? args) (not (ar-false? (car args))))
+                     ((car args) h))
+                (if (and (pair? args) (pair? (cdr args)))
+                     (hash-table-put! table-vivifier h (cadr args)))
 ;(xdef table (lambda args

Inspired by Perl’s autovivification feature, this hack allows new values to be automatically created in a table when a lookup doesn’t find the key already present.

In Perl, the kind of value to create is determined by syntax. Since in Arc a list reference vs. a table reference etc. has the same syntax, we’ll instead specify a function to be called to create the new value, if a key isn’t yet in a table.

arc> (= x (table nil (fn () 0)))
arc> (++ x!a)
arc> x
#hash((a . 1))
arc> (= y (table nil (fn () (table))))
arc> (set y!a!b)
arc> y
#hash((a . #hash((b . t))))

Unlike having a default value for the table, the vivified value is always stored in the table.

arc> (= z (table nil (fn () 0)))
arc> z!a
arc> z
#hash((a . 0))

This means it doesn't make sense to use this for tables where you want to be able to check whether a value is already in the table or not.

Get this hack

Using the hackinator:

$ hack \ \


This patch is in the public domain. The combination of this patch and Arc may be redistributed or modified under the same terms as Arc itself.

Contact me

Twitter: awwx
Email: andrew.wilcox [at]