如何在ClojureScript中运行eval并可以访问调用eval的命名空间? [英] How can I run eval in ClojureScript with access to the namespace that is calling the eval?

查看:95
本文介绍了如何在ClojureScript中运行eval并可以访问调用eval的命名空间?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a library of functions which I want to let users play with in the browser.

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

So I want to set up a situation like this :

我正在用figwheel和devcards开发.

I'm developing with figwheel and devcards.

在主要core.cljs中,我require我的库中有各种函数,所以它们都在作用域之内.

In the main core.cljs I require various functions from my library, so they're all in scope.

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

Now I want to let the user enter some code which calls that library.

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

I see how I can run that code with eval, but I can't see how to make my library functions visible to the code being evaled.

我对所看到的大多数文档感到困惑(例如,

And I'm confused by most of the documentation I'm seeing about this (eg. How can I make functions available to ClojureScript's eval?)

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

Is it possible? And if so, does anyone have a simple example of it being done?

欢呼

菲尔

推荐答案

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

Yes, it is possible to provide access to an ambient / pre-compiled library used by evaluated code.

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

First, you must ensure that the functions in your library are available in the JavaScript runtime. In other words, avoid :advanced optimization, as this will eliminate functions not called at compile time (DCE). Self-hosted ClojureScript is compatible with :simple.

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

Second, you need to make the analysis metadata available to the self-hosted compiler that will be running in the browser (either making use of cljs.js/load-analysis-cache! or an optional argument to cljs.js/empty-state).

下面是一个最小的项目,说明了如何执行此操作(以及 https://github.com/mfikes /ambient ):

A minimal project illustrating how to do this is below (and also at 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命名空间中调用函数.

Usage

We have a main.core namespace which provides an evaluate function, and this example will show how to call functions in an ambient / pre-compiled library.core namespace.

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

First, start up a browser REPL via

clj -m cljs.main

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

At the REPL, load our main namespace by evaluating

(require 'main.core)

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

Test that we can evaluate some code:

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

这应该打印

{:ns cljs.user, :value 5}

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

Now, since main.core required library.core, we can call functions in that namespace. Evaluating this at the REPL will yield 11:

(library.core/my-inc 10)

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

Now, let's try to use this "ambient" function from self-hosted 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环境中可用并且确实可以被调用并产生正确的答案,您也会从自托管编译器收到警告,告知它对此一无所知命名空间.

In short, what is going on is that even though library.core.my_inc is available in the JavaScript environment, and can indeed be called, producing the correct answer, you get warnings from the self-hosted compiler that it knows nothing about this namespace.

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

This is because the compiler analysis metadata is not in the main.core/state atom. (The self-hosted compiler has its own analysis state, held in that atom in the JavaScript environment, which is separate from the JVM compiler analysis state, held via Clojure in the Java environment.)

注意:如果相反,我们是由自托管编译器编译的library.core源代码(可能是通过使用main.core/evaluate评估"(require 'library.core)"),以及正确定义了可以检索该源代码的cljs.js/*load-fn*,一切都会好起来,而编译器分析元数据包含在main.core/state.中,但是此示例是关于在library.core中调用环境/预编译的函数.

Note: If we instead had the source for library.core compiled by the self-hosted compiler (by perhaps by using main.core/evaluate to eval "(require 'library.core)", along with properly defining a cljs.js/*load-fn* that could retrieve this source, things would be good, and the compiler analysis metadata would be in main.core/state. But this example is about calling ambient / pre-compiled functions in library.core.

我们可以通过使用cljs.js/load-analysis-cache!来加载与library.core名称空间关联的分析缓存来解决此问题.

We can fix this by making use of cljs.js/load-analysis-cache! to load the analysis cache associated with the library.core namespace.

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

This example code embeds this analysis cache directly in the code by employing a macro that snatches the analysis cache from the JVM-based compiler. You can transport this analysis cache to the browser by any mechanism you desire; this just illustrates one way of simply embedding it directly in the shipping code (it's just data).

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

Go ahead and evaluate the following, just to see what the analysis cache for that namespace looks like:

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

如果您致电

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

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

this analysis cache will be loaded for use by the self-hosted compiler.

现在,如果您进行评估

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

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

you won't see any warnings and this will be printed:

{:ns cljs.user, :value 11}

此外,由于自托管编译器现在具有用于libraray.core的分析元数据,因此它可以适当地发出警告,例如

Furthermore, since the self-hosted compiler now has the analysis metadata for libraray.core, it can properly warn on arity errors, for example

(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!修复它.如果您知道总是要在启动时加载缓存,则可以做一些简单的事情,利用cljs.js/empty-state的可选参数在初始化时加载该缓存:

The above illustrates what happens when you don't have the analyzer cache present for a namespace and how to fix it using cljs.js/load-analysis-cache!. If you know that you will always want to load the cache upon startup, you can simply things, making use of an optional argument to cljs.js/empty-state to load this cache at initialization time:

(defn init-state [state]
  (assoc-in state [:cljs.analyzer/namespaces 'library.core]
    (analyzer-state 'library.core)))

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

其他项目

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

Other Projects

A few (more elaborate) projects that make library functions available to self-hosted ClojureScript in the browser:

  • Klangmeister
  • power-turtle
  • life-demo

这篇关于如何在ClojureScript中运行eval并可以访问调用eval的命名空间?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆