evalive

1.0.0


Various eval functions and macros for Clojure

dependencies

org.clojure/clojure
1.2.0

dev dependencies

lein-clojars
0.5.0-SNAPSHOT
jline
0.9.94
swank-clojure
1.2.1
marginalia
0.5.0-alpha



(this space intentionally left blank)
 

by Fogus - Feb. 9, 2011

http://github.com/fogus/evalive

Various eval functions and macros.

(ns evalive.core)

evalive

Map-every-other. Works like map except only on every other element.

(defn- map-eo
  [f & colls]
  (apply map
         #(%1 %&)
         (cycle [first #(apply f %)])
         colls))

Looks up the value of a symbol at the time of compilation and quotes it. This will be useful later on.

(def ^{:private true
       :doc }
  compile-time-lookup
  #(do `'~%))

Public API

When called, lexical-context returns a map of symbol → value of the current lexical bindings where the call occurred. For example:

(let [a 1]
  (let [b 2]
    (lexical-bindings)))

returns

{a 1, b 2}

TODO Explore the utility of a version that takes a map and returns the merge of the supplied map and the lexical bindings.

(defmacro lexical-context
  []
  (let [symbols (keys &env)]
    (zipmap (map (fn [sym] `(quote ~sym))
                 symbols)
            symbols)))

Defines the public interface to evilive's "contextual eval"® facilities. In a nutshell, contextual eval refers to perform an eval that refers to lexical bindings in addition to namespace bindings. You see, the core eval function, like and function, is not privy to the lexical context in which it is run and is therefore of limited scope in its usefulness. However, evalive enhances the stock eval by building a lexical context into the form under evaluation from various structures.

(defprotocol Evil
  (evil [this form]))

lexical contexts defined in maps

(extend-type java.util.Map
  Evil
  (evil [this form]
    (eval
     `(let [~@(mapcat (fn [[k v]] [k `'~v])
                      this)]
        ~form))))

lexical contexts defined in sequentials (e.g. lists, vectors)

(extend-type java.util.List
  Evil
  (evil [this form]
    (eval
     `(let [~@(map-eo compile-time-lookup this)]
        ~form))))

lexical contexts defined in arrays

(extend-type (Class/forName "[Ljava.lang.Object;")
  Evil
  (evil [this form]
    (evil (seq this) form)))

Provides a simple way to obtain a map of the lexical context based on the result of a destructuring operation. That is, the typical call to the destructure function will operate along the lines of:

(destructure '[[_ _ x _ _] [1 2 3 4 5]])

;=> [V [1 2 3 4 5]
     _ (nth V 0 nil)
     _ (nth V 1 nil)
     x (nth V 2 nil)
     _ (nth V 3 nil)
     _ (nth V 4 nil)]

whereby the form returned contains the operations needed to pull apart (i.e. destructure) the data structure under examination. However, destro will instead resolve the values of the destructuring operation, including any intermediate bindings, as below:

(destro [a b [c d & e] :as Z]
        [1 2 [3 4 5 6 7 8]])

;=> {vec__2330 [1 2 [3 4 5 6 7 8]],
     a 1,
     b 2,
     vec__2331 [3 4 5 6 7 8],
     c 3,
     d 4,
     e (5 6 7 8),
     Z [1 2 [3 4 5 6 7 8]]}

This will also operate as expected within a lexical context:

(let [c [1 2]]
  (destro [a b] c))

;=> {c [1 2],
     vec__2336 [1 2],
     a 1,
     b 2}
(defmacro destro
  [binds form]
  `(let [~binds ~form]
     (lexical-context)))
(comment
  (evil '{message "Hello", place "Cleveland"}
        '(println message place))

  ; Hello Cleveland
  
  (destro [message place] ["Hello" "Cleveland"])
  ;=> {vec__2438 [Hello Cleveland], message Hello, place Cleveland}
  
  (evil (destro [message place] ["Hello" "Cleveland"])
        '(println message place))

  ; Hello Cleveland
)