I want a smart table initializer. Here's a spec (5 tests): (let a nil
(test-iso "#1 - inittab returns table with values"
(obj 1 2 3 4)
(inittab a 1 2 3 4)))
(let a (obj 1 2)
(inittab a 3 4)
(test-iso "#2 - inittab retains preexisting values"
(obj 1 2 3 4)
a))
(let a (obj 1 2)
(test-iso "#3 - inittab doesn't overwrite existing values"
(obj 1 2 3 4)
(inittab a 1 5 3 4)))
(with (inittab-test nil x 0)
(def inittab-test()
(w/table ans
(or= ans.x (table))
(or= ans.x.3 4)
(or= ans.x!foo (table))
(or= ans.x!bar (table))))
(with (a (table) x 3 y 'bar)
(test-iso "#4 - inittab handles quoted and unquoted keys"
((inittab-test) 0)
(inittab a.0 x (+ 3 1) 'foo (table) y (table)))))
(with (a nil x 3)
(inittab a x (+ x 1))
(test-iso "#5 - inittab evaluates keys and values"
(obj 3 4)
a))
Here's my solution: (mac inittab(place . l)
`(do
(or= ,place (table))
(init-table ,place ,@l)))
(def init-table (table . data)
(each (k v) (pair data) (or= (table k) v))
table)
The only wart here is that the interface of init-table isn't like fill-table. It doesn't take just 2 args. My question is: can you think of a way to make inittab work if I make init-table look like fill-table? (def init-table2 (table data)
(each (k v) (pair data) (or= (table k) v))
table)
==I've tried 2 approaches. The first is to try to avoid init-table by using eval. Eval'ing just values works fine: (mac inittab(place . l)
`(do
(or= ,place (table))
(each (k v) (pair ',l)
(or= (,place k) eval.v))
,place))
Test 5 will fail, and test 4 will pass if the final line contains literal keys: (inittab a.0 3 (+ 3 1) foo (table) bar (table))
However, taking this approach further with eval.k doesn't work. (mac inittab(place . l)
`(do
(or= ,place (table))
(each (k v) (pair ',l)
(or= (,place eval.k) eval.v))
,place))
Test 4: reference to undefined identifier: _fooTest 5: reference to undefined identifier: _x Eval not obeying lexical environments seems like a wart, but mzscheme doesn't seem to handle it either.. mzscheme> (let ((k 3)) (eval 'k))
reference to undefined identifier: k
mzscheme> (define k 3)
mzscheme> (eval 'k)
3
arc> (let k 3 (eval 'k))
Error: "reference to undefined identifier: _k"
arc> (= k 3)
3
arc> (eval 'k)
3
So it seems we need that function boundary to do eval right.== Second approach. The obvious way to avoid the rest arg in inittab is as follows: (mac inittab(place . l)
`(do
(or= ,place (table))
(init-table2 ,place ,l)))
But ,l now wants to evaluate as a function ("Function call on inappropriate object 1 (2 3 4)"), and if we quote it we won't get evaluation at all. Here's another approach: (mac inittab(place . l)
`(do
(or= ,place (table))
(init-table2 ,place (apply list ',l))))
No dice. Tests 4 and 5 fail.So it seems we need the ,@ operator to do eval right. == I'm reminded of Alan Kay's criticism of lisp's special forms: "The pure language was supposed to be based on functions, but its most important components---such as lambda expressions quotes, and conds--were not functions at all, and instead were called special forms. Landin and others had been able to get quotes and cons in terms of lambda by tricks that were variously clever and useful, but the flaw remained in the jewel. Why on earth call it a functional language? Why not just base everything on FEXPRs and force evaluation on the receiving side when needed?" (http://gagne.homedns.org/~tgagne/contrib/EarlyHistoryST.html) |