如何在ClojureScript中运行eval并可以访问调用eval的命名空间?

时间:2018-07-28 17:52:36

标签: namespaces eval clojurescript

我有一个函数库,我想让用户在浏览器中使用。

所以我想设置一个这样的情况:

我正在用Figwheel和devcards开发。

在主要core.cljs中,我require从我的库中提供了各种功能,因此它们都在范围内。

现在,我想让用户输入一些调用该库的代码。

我了解了如何使用eval运行该代码,但看不到如何使所逃避的代码可见我的库函数。

我对所看到的大多数文档感到困惑(例如How can I make functions available to ClojureScript's eval?

有可能吗?如果是这样,有人能举个简单的例子吗?

欢呼

菲尔

1 个答案:

答案 0 :(得分:6)

是的,可以提供对评估代码使用的环境/预编译库的访问。

首先,必须确保库中的函数在JavaScript运行时中可用。换句话说,请避免:advanced优化,因为这将消除编译时(DCE)未调用的函数。自托管的ClojureScript与:simple兼容。

第二,您需要使分析元数据可用于将在浏览器中运行的自托管编译器(使用cljs.js/load-analysis-cache!cljs.js/empty-state的可选参数)。

下面(以及在https://github.com/mfikes/ambient上)的一个最小项目说明了如何做到这一点:

项目代码

src/main/core.cljs

(ns main.core
  (:require-macros [main.core :refer [analyzer-state]])
  (:require [cljs.js]
            [library.core]))

(def state (cljs.js/empty-state))

(defn evaluate [source cb]
  (cljs.js/eval-str state source nil {:eval cljs.js/js-eval :context :expr} cb))

(defn load-library-analysis-cache! []
  (cljs.js/load-analysis-cache! state 'library.core (analyzer-state 'library.core))
  nil)

src/main.core.clj

(ns main.core
  (:require [cljs.env :as env]))

(defmacro analyzer-state [[_ ns-sym]]
  `'~(get-in @env/*compiler* [:cljs.analyzer/namespaces ns-sym]))

src/library/core.cljs

(ns library.core)

(defn my-inc [x]
  (inc x))

用法

我们有一个main.core命名空间,它提供一个evaluate函数,该示例将说明如何在环境/预编译的library.core命名空间中调用函数。

首先,通过以下方式启动浏览器REPL

clj -m cljs.main

在REPL上,通过评估加载主命名空间

(require 'main.core)

测试我们可以评估一些代码:

(main.core/evaluate "(+ 2 3)" prn)

这应该打印

{:ns cljs.user, :value 5}

现在,由于main.core需要library.core,因此我们可以在该命名空间中调用函数。在REPL上对此进行评估将得出11

(library.core/my-inc 10)

现在,让我们尝试通过自托管的ClojureScript使用此“环境”功能:

(main.core/evaluate "(library.core/my-inc 10)" prn)

您将看到以下内容

WARNING: No such namespace: library.core, could not locate library/core.cljs, library/core.cljc, or JavaScript source providing "library.core" at line 1
WARNING: Use of undeclared Var library.core/my-inc at line 1
{:ns cljs.user, :value 11}

简而言之,发生的事情是即使library.core.my_inc在JavaScript环境中可用并且确实可以被调用并产生正确的答案,您也会从自托管编译器收到警告,告知它什么都不知道关于这个名称空间。

这是因为编译器分析元数据不在main.core/state原子中。 (自托管的编译器具有自己的分析状态,该状态保存在JavaScript环境中的那个原子中,与通过Java环境中的Clojure保存的JVM编译器分析状态分开。)

  

注意:如果相反,我们是由自托管编译器编译的library.core的源代码(可能是通过使用main.core/evaluate来评估"(require 'library.core)"以及正确定义{{1 }}可以检索此源,情况会很好,并且编译器分析元数据包含在cljs.js/*load-fn*中,但是此示例涉及在{{1}中调用环境/预编译函数}。

我们可以通过使用main.core/state.来加载与library.core命名空间关联的分析缓存来解决此问题。

此示例代码通过使用从基于JVM的编译器抢夺分析缓存的宏,将该分析缓存直接嵌入到代码中。您可以通过任何所需的机制将此分析缓存传输到浏览器。这只是说明了一种直接将其直接嵌入到运输代码(只是数据)中的方法。

继续并评估以下内容,以查看该命名空间的分析缓存是什么样的:

cljs.js/load-analysis-cache!

如果您致电

library.core

此分析缓存将被加载以供自托管编译器使用。

现在,如果您评估

(main.core/analyzer-state 'library.core)

您将不会看到任何警告,并且会显示以下警告:

(main.core/load-library-analysis-cache!)

此外,由于自托管编译器现在具有用于(main.core/evaluate "(library.core/my-inc 10)" prn) 的分析元数据,因此它可以正确地发出警告,例如

{:ns cljs.user, :value 11}

将导致此打印:

libraray.core

以上内容说明了当您不存在用于名称空间的分析器缓存时发生的情况以及如何使用(main.core/evaluate "(library.core/my-inc 10 12)" prn) 对其进行修复。如果您知道总是要在启动时加载缓存,则可以做一些简单的事情,利用WARNING: Wrong number of args (2) passed to library.core/my-inc at line 1 的可选参数在初始化时加载该缓存:

cljs.js/load-analysis-cache!

其他项目

一些(更详细的)项目使库函数可用于浏览器中的自托管ClojureScript: