2016-07-26 73 views
2

我写了一个专门的函数构造,它在引擎之下实际上只是一个Clojure函数。所以基本上我有一个功能(类似于fn)和一个调用我的专门功能的功能(类似于CL的funcall)。有没有办法在编译时有值的元数据?

我的构造函数分配元数据(在编译时),所以我可以区分“我的”函数和其他/正常的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问题?我欢迎建议)。

+0

据我了解,你将元数据附加到运行时对象(功能)。很显然,在编译时不能访问运行时对象的元数据(不需要评估它) – OlegTheCat

+0

@OlegTheCat是的,这就是问题所在(尽管iirc实际的Clojure元数据除了在词法环境中绑定时才工作)。在编译时没有办法让这些数据可用(通过元数据或其他方式)? – MasterMastic

+0

为什么你不把所有的魔力放在myfun宏里面?为什么你不能在那里添加所有的检查? – murphy

回答

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 [email protected](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 [[email protected]#])) 

        ;; 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])) 
相关问题