Created
September 24, 2010 16:27
-
-
Save mrBliss/595634 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defn replace-symbols | |
"Given a map of replacement pairs and a form, returns a (nested) | |
form with any symbol = a key in smap replaced with the corresponding | |
val in smap." | |
[smap form] | |
(if (sequential? form) | |
(map (partial replace-symbols smap) form) | |
(get smap form form))) | |
(defmacro lazy-let | |
"A lazy version of let. It doesn't evaluate any bindings until they | |
are needed. No more nested lets and it-lets when you have many | |
conditional bindings." | |
[bindings & body] | |
(let [locals (take-nth 2 bindings) | |
local-forms (take-nth 2 (rest bindings)) | |
smap (zipmap locals (map (fn [local] `(first ~local)) locals)) | |
bindings (->> (map (fn [lf] | |
`(lazy-seq (list ~(replace-symbols smap lf)))) | |
local-forms) (interleave locals) vec) | |
body (replace-symbols smap body)] | |
(conj body bindings 'let))) | |
;; Returns yes, because x and y can be evaluated | |
(let [s [[1 2] 3] | |
x (nth s 0) | |
y (inc (second x))] | |
(if (= 2 (count s)) | |
(if (vector? x) | |
(if (number? y) | |
"yes" | |
"fail3") | |
"fail2") | |
"fail1")) | |
;; Throws an exception because we cannot increment nil | |
(let [s nil | |
x (nth s 0) | |
y (inc (second x))] | |
(if (= 2 (count s)) | |
(if (vector? x) | |
(if (number? y) | |
"yes" | |
"fail3") | |
"fail2") | |
"fail1")) | |
;; If we use lazy-let instead of let, the locals aren't evaluated | |
;; until they are needed. The first if fails, since the count of s (= | |
;; nil) is 0, so there is no need to evaluate x and y. | |
(lazy-let [s nil | |
x (nth s 0) | |
y (inc (second x))] | |
(if (= 2 (count s)) | |
(if (vector? x) | |
(if (number? y) | |
"yes" | |
"fail3") | |
"fail2") | |
"fail1")) | |
;; This is how the form above will be macroexpanded | |
;; (without the clojure.core/ qualifiers) | |
(let [s (lazy-seq (list nil)) | |
x (lazy-seq (list (nth s 0))) | |
y (lazy-seq (list (inc (second x))))] | |
(if (= 2 (count (first s))) | |
(if (vector? (first x)) | |
(if (number? (first y)) | |
"yes" | |
"fail3") | |
"fail2") | |
"fail1")) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here is a version based on delays that supports any kind of bound expr (not just sequences).
It works by wrapping bound exprs into
(delay ...)
then overriding the corresponding binding syms withsymbol-macrolet
so that they expand to(deref ...)
.More: https://gist.github.com/TristeFigure/09c1c6309b70ccdb5ae164606e3876e9