| I just watched the "Clojure for Lisp Programmers" talk and was impressed by the conj function and the generic sequence interface to lists, maps and vectors. In Arc, the function map is accepts both hashtables and lists, but those are hardcoded. If we overload car and cdr, we can treat other things (like lazy lists) as lists: (map my-function my-lasy-list)
but then the whole my-lazy-list would be forced evaluated. How can we ensure that map does not only accept lazy lists, but also return lazy lists?With dynamic bindings we could change the behaviour of map, so it returns a lazy list: (dyn-let cons lazy-cons
(map my-function my-lazy-list))
but dynamic variables are considered harmful and hard to reason about.To avoid dynamic binding at all costs, I propose a reification of closures, the with-closure function. It takes a function and
some bindings and returns the function with the added bindings. ((with-closure (let x nil (fn () x)) 'x t))
=> t
(with-closure map 'cons lazy-cons)
Because map is a recursive function, the modified version should call the modified version, and not the global map, so it gets a bit more complicated: (let lazy-map map
(= lazy-map
(with-closure lazy-map 'cons lazy-cons)
lazy-map
(with-closure lazy-map 'map lazy-map)))
This could be wrapped in a macro: (mac rwith-closure (fun . mappings)
(w/uniq newfun
`(let ,newfun (with-closure ,fun ,@mappings)
(= ,newfun (with-closure ,newfun ',fun ,newfun)))))
This would allow us to write a lazy-list modifier that makes a function operate on lazy lists, or more generally to retrofit abstractions onto previously defined functions. |