Macros 双Clojure/ClojureScript库设计和宏

Macros 双Clojure/ClojureScript库设计和宏,macros,clojure,leiningen,clojurescript,Macros,Clojure,Leiningen,Clojurescript,假设我有一个库xx,其名称空间为xx.core,我用纯Clojure编写它,打算同时针对Clojure和ClojureScript。事实上,这样做的方式似乎是使用。到目前为止,一切顺利这个前提现在已经过时了。lein cljsbuild已被弃用,取而代之的是,还有许多其他名称空间/宏ClojureScript增强功能。请参阅以下内容。 假设xx有一堆vars,我希望它的用户,无论是Clojure还是ClojureScript,都能使用它们。这些变量可以分为三类 宏 xx中不依赖宏的函数/其他变

假设我有一个库xx,其名称空间为
xx.core
,我用纯Clojure编写它,打算同时针对Clojure和ClojureScript。事实上,这样做的方式似乎是使用。到目前为止,一切顺利这个前提现在已经过时了。lein cljsbuild已被弃用,取而代之的是,还有许多其他名称空间/宏ClojureScript增强功能。请参阅以下内容。

假设xx有一堆vars,我希望它的用户,无论是Clojure还是ClojureScript,都能使用它们。这些变量可以分为三类

  • xx中不依赖宏的函数/其他变量(我将调用这些类型1变量)
  • 恰好依赖于xx中的宏的函数/其他变量(我将调用这些类型2变量)
但是,由于ClojureScript要求宏在其特殊的
.clj
名称空间中与常规的
.cljs
名称空间分离,因此所有宏都必须与
xx.core
中的所有其他变量分离

但是,其他一些变量(2型变量)的实现碰巧依赖于这些宏

(可以肯定的是,使用ClojureScript的
use macro
require macro
似乎只能访问宏。刚才我测试了这一点;我尝试将所有内容(宏、1型变量和2型变量)保存在一个
xx/core.clj
文件中,并使用
在ClojureScript测试文件中引用它。)(:use macro xx.core:only[…])
。然后,编译器会为ClojureScript文件引用的
xx.core
中的每个非宏变量发出
警告:使用未声明的Var
消息。)

在这种情况下人们倾向于做什么?在我看来,我唯一能做的就是…将库的公共API分成三个名称空间:一个用于1型变量,一个用于宏,一个用于2型变量。类似于…
xx.core
xx.macro
,和
xx.util


当然,这种情况很糟糕,因为现在xx的任何用户(在Clojure或ClojureScript中)都必须知道每个var(可能有几十个)是否恰好取决于其实现中的宏,以及它所属的命名空间。如果我只针对Clojure,则不需要这样做。如果我想同时针对Clojure和ClojureScript,现在真的是这样吗?

您似乎正确地理解了这种情况:)

关于:“xx的任何用户(无论是Clojure还是ClojureScript)都必须知道每个var(可能有几十个)是否恰好依赖于其实现中的宏,以及它所属的名称空间。”


您可以再添加两个名称空间,api.clj和api.cljs,这两个名称空间为该api的每个变量都包含适当的namspace,并且可以从这个决定中减轻一些痛苦。不过,这个领域似乎还是很新的

这个问题的前提在几年前基本上已经过时了。我正在尽我的一份力量,通过这次更新,不让过时的信息污染网络

ClojureScript与Clojure不同,它通常在独立于运行时的编译阶段编译宏。还有很多附带的竞争。然而,由于一些改进,情况已经大大改善

由于,Clojure和ClojureScript现在支持,这使得可以在同一个
.cljc
文件中为Clojure、ClojureScript、Clojure CLR或所有三个文件定义宏和函数:
#?(:clj…,:cljs…,:cljr…,:default…
)。仅此一点就缓解了大部分问题

此外,ClojureScript本身现在对
ns
进行了一些增强,从而消除了命名空间用户的许多其他附带复杂性。它们现在记录在中。它们包括隐式宏加载、内联宏规范和自动别名
clojure
名称空间:

隐式宏加载:如果需要或使用名称空间,并且该名称空间本身需要或使用其自己名称空间中的宏,则将隐式需要宏或使用相同的规范。此外,在这种情况下,宏变量可能包含在:refere或:only规范中。这通常会简化库的使用,因此使用名称空间不必关心显式区分某些变量是函数还是宏。例如:

(ns testme.core
  (:require [foo.core :as foo :refer [foo-fn] :include-macros true]
            [woz.core :as woz :refer [woz-fn] :refer-macros [apple jax]]))
(ns testme.core (:require [clojure.test]))
(ns testme.core(:require[cljs.test:as test:refere[test var deftest]])
将导致test/is正确解析,test var函数和deftest宏可用

内联宏规范:为方便起见,:require可以提供:include macros true或:refere macros[syms…​]. 两个desugar都会加载到表单中,这些表单会显式加载包含宏的匹配Clojure文件。(这独立于内部需要的命名空间是否需要或使用自己的宏。)

(ns testme.core
  (:require [foo.core :as foo :refer [foo-fn] :include-macros true]
            [woz.core :as woz :refer [woz-fn] :refer-macros [apple jax]]))
(ns testme.core (:require [clojure.test]))
糖是用来做什么的

(ns testme.core
  (:require [foo.core :as foo :refer [foo-fn]]
            [woz.core :as woz :refer [woz-fn]])
  (:require-macros [foo.core :as foo]
                   [woz.core :as woz :refer [apple jax]]))
自动别名
clojure
名称空间
:如果需要或使用不存在的
clojure.
名称空间,并且存在匹配的cljs.*名称空间,则将加载
cljs.
名称空间,并自动从
clojure.
名称空间到
cljs.
名称空间建立别名ace。例如:

(ns testme.core
  (:require [foo.core :as foo :refer [foo-fn] :include-macros true]
            [woz.core :as woz :refer [woz-fn] :refer-macros [apple jax]]))
(ns testme.core (:require [clojure.test]))
将自动转换为

(ns testme.core (:require [cljs.test :as clojure.test]))`
最后,ClojureScript现在还有第二个目标:自引导的、自托管的ClojureScript,也称为CLJS中的CLJS。与JVM编译器上的CLJS不同,自引导的ClojureScript编译器实际上可以编译宏!源文件中仍然强制分离,但其REPL可以很好地将它们与函数混合运行

Mike Fikes就Clojure–Cloj的这些和其他问题撰写了一系列有价值的文章