--- 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))) h))) ;(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))) #hash()
arc> (++ x!a) 1
arc> x #hash((a . 1))
arc> (= y (table nil (fn () (table)))) #hash()
arc> (set y!a!b) t
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))) #hash()
arc> z!a 0
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.
Using the hackinator:
$ hack \ ycombinator.com/arc/arc3.1.tar \ awwx.ws/table-vivifier0.patch
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.