hara.expression interchange between code and data

Author: Chris Zheng  (z@caudate.me)
Date: 29 June 2017
Repository: https://github.com/zcaudate/hara
Version: 2.5.10

hara.expression allow transformations of forms into functions and predicates in a succint way.

1    expression.compile

Add to project.clj dependencies:

[im.chit/hara.expression.compile "2.5.10"]

hara.expression.compile looks at compiler time expressions



applym ^

allow macros to be applied to arguments just like functions

v 2.1
(defmacro applym
  [macro & args]
  (cons macro (#'clojure.core/spread (map eval args))))
link
(applym const '((+ 1 2))) => 3 (macroexpand '(applym const '((+ 1 2)))) => 3

const ^

converts an expression into a constant at compile time

v 2.1
(defmacro const
  [body]
  (eval body))
link
(const (+ 1 2)) => 3 (macroexpand '(const (+ 1 2))) => 3

2    expression.form

Add to project.clj dependencies:

 [im.chit/hara.expression.form "2.5.10"]

hara.expression.form provides methods that transform forms into anonymous functions



form-apply ^

applies a list as a function to an argument vector

v 2.1
(defn form-apply
  [form args]
  (apply (form-fn form) args))
link
(form-apply '(+ 1 %1 %2) [2 3]) => 6

form-eval ^

evaluates a list as a functions and to a set of arguments.

v 2.1
(defn form-eval
  [form & args]
  (apply (form-fn form) args))
link
(form-eval '(+ 1 %1 %2) 2 3) => 6

form-fn ^

creates a function out of a list

v 2.1
(defn form-fn
  [form]
  (try (let [fform (form-prep form)]
         (with-meta (eval fform) (meta fform)))
    (catch clojure.lang.Compiler$CompilerException e
      (error e (str "Cannot evaluate form: " form)))))
link
(def my-inc (form-fn '(+ 1 %))) (my-inc 1) => 2 (meta my-inc) => {:source "#(+ 1 %)n"}

form-require ^

makes sure that the namespace is loaded for a particular symbol

v 2.1
(defn form-require
  [x]
  (if (symbol? x)
    (do (if-let [nsp (.getNamespace ^clojure.lang.Symbol x)]
          (require (symbol nsp)))
        x)
    x))
link
(form-require 'cons) => 'cons (form-require 'clojure.core/cons) => 'clojure.core/cons

3    expression.load

Add to project.clj dependencies:

 [im.chit/hara.expression.load "2.5.10"]

hara.expression.load provides a simple mechanism for loading code that can be in a form



load ^

seeds an initial map using forms

v 2.1
(defn load
  ([m] (load m :_init))
  ([m init]
     (cond (keyword? init)
           (dissoc (load m (get m init)) init)

           (vector? init)
           (let [pairs (partition 2 init)]
             (reduce load-single m pairs)))))
link
(load {:a 1} [:b '(inc (:a %)) :c '(+ (:a %) (:b %))]) => {:a 1 :b 2 :c 3}

4    expression.shorthand

Add to project.clj dependencies:

 [im.chit/hara.expression.shorthand "2.5.10"]

hara.expression.shorthand provides methods that work with code as data



call-> ^

indirect call, takes `obj` and a list containing either a function, a symbol representing the function or the symbol `?` and any additional arguments. Used for calling functions that have been stored as symbols.

v 2.1
(defn call->
  [obj [ff & args]]
  (cond (nil? ff)     obj
        (list? ff)    (recur (call-> obj ff) args)
        (vector? ff)  (recur (get-in obj ff) args)
        (keyword? ff) (recur (get obj ff) args)
        (fn? ff)      (apply ff obj args)
        (symbol? ff)  (if-let [f (do
                                   (resolve-ns ff)
                                   (suppress (resolve ff)))]
                        (apply call f obj args)
                        (recur (get obj ff) args))
        :else         (recur (get obj ff) args)))
link
(call-> 1 '(+ 2 3 4)) => 10 (call-> 1 '(< 2)) => true (call-> {:a {:b 1}} '((get-in [:a :b]) = 1)) => true

check ^

checks

v 2.1
(defn check
  ([obj chk]
   (or (= obj chk)
       (-> (get-> obj chk) not not)))
  ([obj sel chk]
    (check (get-> obj sel) chk)))
link
(check 2 2) => true (check 2 even?) => true (check 2 '(> 1)) => true (check {:a {:b 1}} '([:a :b] (= 1))) => true (check {:a {:b 1}} :a vector?) => false (check {:a {:b 1}} [:a :b] 1) => true

check-> ^

shorthand ways of checking where `m` fits `prchk`

v 2.1
(defn check->
  [obj pchk]
  (cond (vector? pchk)
        (apply check-all obj pchk)

        (set? pchk)
        (or (some true? (map #(check-> obj %) pchk))
            false)

        :else
        (check obj pchk)))
link
(check-> {:a 1} :a) => true (check-> {:a 1 :val 1} [:val 1]) => true (check-> {:a {:b 1}} [[:a :b] odd?]) => true

check-all ^

returns `true` if `obj` satisfies all pairs of sel and chk

v 2.1
(defn check-all
  [obj & pairs]
  (every? (fn [[sel chk]]
            (check obj sel chk))
          (partition 2 pairs)))
link
(check-all {:a {:b 1}} :a #(instance? clojure.lang.IPersistentMap %) [:a :b] 1) => true

check?-> ^

tests obj using prchk and returns `obj` or `res` if true

v 2.1
(defn check?->
  ([obj prchk] (check?-> obj prchk true))
  ([obj prchk res]
     (suppress (if (check-> obj prchk) res))))
link
(check?-> :3 even?) => nil (check?-> 3 even?) => nil (check?-> 2 even?) => true (check?-> {:id :1} '[:id (= :1)]) => true

eq-> ^

compare if two vals are equal.

v 2.1
(defn eq->
  [obj1 obj2 sel]
  (= (get-> obj1 sel) (get-> obj2 sel)))
link
(eq-> {:id 1 :a 1} {:id 1 :a 2} :id) => true (eq-> {:db {:id 1} :a 1} {:db {:id 1} :a 2} [:db :id]) => true

fn-> ^

constructs a function from a form representation.

v 2.1
(defn fn->
  [form]
  (eval (shorthand-fn-expr form)))
link
((fn-> '(+ 10)) 10) => 20

get-> ^

provides a shorthand way of getting a return value. `sel` can be a function, a vector, or a value.

v 2.1
(defn get->
  [obj sel]
  (cond (nil? sel)    obj
        (list? sel)   (call-> obj sel)
        (vector? sel) (get-in obj sel)
        (symbol? sel) (if-let [f (do (if-let [nsp (.getNamespace ^clojure.lang.Symbol sel)]
                                       (require (symbol nsp)))
                                     (suppress (resolve sel)))]
                        (call f obj)
                        (get obj sel))
        (ifn? sel)    (sel obj)
        :else         (get obj sel)))
link
(get-> {:a {:b {:c 1}}} :a) => {:b {:c 1}} (get-> {:a {:b {:c 1}}} [:a :b]) => {:c 1}

shorthand-fn-expr ^

makes a function expression out of the form

v 2.1
(defn shorthand-fn-expr
  [form]
  (apply list 'fn ['%]
         (list (shorthand-form '% form))))
link
(shorthand-fn-expr '(+ 2)) => '(fn [%] (+ % 2))

shorthand-form ^

makes an expression using `sym`

v 2.1
(defn shorthand-form
  [sym [ff & more]]
  (cond (nil? ff)     sym
        (list? ff)    (recur (shorthand-form sym ff) more)
        (vector? ff)  (recur (list 'get-in sym ff) more)
        (keyword? ff) (recur (list 'get sym ff) more)
        (symbol? ff)  (apply list ff sym more)
        :else         (recur (list 'get sym ff) more)))
link
(shorthand-form 'y '(str)) => '(str y) (shorthand-form 'x '((inc) (- 2) (+ 2))) => '(+ (- (inc x) 2) 2)