有没有办法在编译时获取值的元数据?

时间:2016-07-26 02:56:42

标签: clojure

我编写了一个专门的函数构造,它实际上只是一个Clojure函数。所以基本上我有一个函数(类似于fn)和一个调用我的专用函数的函数(类似于CL' s funcall)。

我的构造函数分配元数据(在编译时),因此我可以区分" my"函数和其他/普通的Clojure函数。

我想要做的是创建一个允许用户编写代码的宏,就像我的函数是正常函数一样。它会通过遍历代码来实现,而在函数调用中,当被调用者是一个专门的函数时,它会改变调用,因此它会使用我的调用者(并且还会注入一些额外的信息)。例如:

(defmacro my-fn [args-vector & body] ...)
(defmacro my-funcall [myfn & args] ...)
(defmacro with-my-fns [& body] ...)

(with-my-fns
  123
  (first [1 2 3])
  ((my-fn [x y] (+ x y))) 10 20)
; should yield:
(do
  123
  (first [1 2 3])
  (my-funcall (my-fn [x y] (+ x y)) 10 20))

我在词汇环境中遇到了问题。例如:

(with-my-fns
  (let [myf (my-fn [x y] (+ x y))]
    (myf))

在这种情况下,当我想写的宏(即with-my-fns)遇到(myf)时,它会将myf视为符号,而我无法访问元数据。它也不是Var,所以我不能resolve它。

我很想知道,因为否则我必须在运行时对几乎所有单个函数调用进行检查。请注意,如果我的值的元数据是实际的Clojure元数据,我真的不在乎;如果它可以与类型系统一起使用,那么它和它一样好。

P.S。我最初只是想问一下词汇环境,但也许我应该知道哪些方法会失败? (或者甚至上面的内容实际上是一个XY问题?我欢迎建议)。

1 个答案:

答案 0 :(得分:1)

正如@OlegTheCat已在评论部分指出,使用元数据的想法不起作用。

但是我可能有一个你可以忍受的解决方案:

(ns cl-myfn.core)

(defprotocol MyCallable
  (call [this magic args]))


(extend-protocol MyCallable
  ;; a clojure function implements IFn
  ;; we use this knowledge to simply call it
  ;; and ignore the magic
  clojure.lang.IFn
  (call [this _magic args]
    (apply this args)))

(deftype MyFun [myFun]
  MyCallable
  ;; this is our magic type
  ;; for now it only adds the magic as first argument
  ;; you may add all the checks here
  (call [this magic args]
    (apply (.myFun this) magic args)))

;;turn this into a macro if you want more syntactic sugar
(defn make-myfun [fun]
  (MyFun. fun))

(defmacro with-myfuns [magic & funs]
  `(do ~@(map (fn [f#]
                ;; if f# is a sequence it is treated as a function call                
                (if (seq? f#)
                  (let [[fun# & args#] f#]
                    `(call ~fun# ~magic [~@args#]))

                  ;; if f# is nonsequential it is left alone
                  f#))
                funs)))


(let [my-prn (make-myfun prn)]
  (with-myfuns :a-kind-of-magic
    123 
    [1 2 3]
    (prn :hello) 
    (my-prn 123)))


;; for your convenience: the macro-expansion 

(let [my-prn (make-myfun prn)]
  (prn (macroexpand-1 '(with-myfuns :a-kind-of-magic
                         123
                         [1 2 3]
                         (prn :hello) 
                         (my-prn 123)))))

输出:

:hello
:a-kind-of-magic 123
(do 123 [1 2 3] (cl-myfn.core/call prn :a-kind-of-magic [:hello]) (cl-myfn.core/call my-prn :a-kind-of-magic [123]))