以 core.async 风格保存变量
我需要做一个大把戏,并且很想听听您的建议。 我需要的是一个宏,它采用普通的 clojure 代码,并加上特殊的“await”形式。 wait 表单仅包含 clojure 代码,并且应该返回代码的返回值。现在,我想要的是,当我运行该宏生成的任何内容时,它应该在第一个“等待”表单需要评估时停止执行。
然后,它应该将迄今为止在其范围内定义的所有变量转储到数据库(我将忽略并非所有 Clojure 类型都可以序列化到 EDN 的问题,例如函数不能),以及它所在位置的一些标记 然后,
如果我想再次运行此代码(可能在另一台机器上,另一天) - 它将从数据库读取其状态并在停止处继续。
因此我可以,例如:
(defexecutor my-executor
(let [x 7
y (await (+ 3 x))]
(if (await (> y x))
"yes"
"no")))
现在,当我这样做时:
(my-executor db-conn "unique-job-id")
第一次我应该得到一个特殊的返回值,例如
:deferred
第二次也应该像这样,只有第三次才应该是真正的返回值回来了。
我的问题不是如何编写这样的执行器,而是如何从宏内收集有关所有声明变量的信息以便能够存储它们。后来我也想在继续执行的时候重新建立它们。当然,await 形式可以嵌套:)
我查看了 core.async 源代码,因为它在里面做了类似的事情,但我在那里发现的东西让我颤抖 - 似乎他们使用 Clojure AST 分析器来获取这个信息。这真的有这么复杂吗?我知道宏内有 &env 变量,但不知道如何在这种情况下使用它。任何帮助将不胜感激。
还有一件事。请不要问我为什么需要这个或者有不同的方法来解决问题 - 我想要这个特定的解决方案。
I need to do a big trick and am keen on hearing your suggestions.
What I need is a macro that takes ordinary clojure code peppered with a special "await" form. The await forms contains only clojure code and are supposed to return the code's return value. Now, what I want is that when I run whatever is being produced by this macro, it should stop executing when the first "await" form is due for evaluation.
Then, it should dump all the variables defined in its scope so far to the database (I will ignore the problem that not all Clojure types can be serialised to EDN, e.g. functions can't), together with some marker of the place it has stopped in.
Then, if I want to run this code again (possibly on a different machine, another day) - it will read its state from the DB and continue where it stopped.
Therefore I could have, for example:
(defexecutor my-executor
(let [x 7
y (await (+ 3 x))]
(if (await (> y x))
"yes"
"no")))
Now, when I do:
(my-executor db-conn "unique-job-id")
the first time I should get a special return value, something like
:deferred
The second time it should be like this as well, only the third time a real return value should be returned.
The question I have is not how to write such executor, but rather how to gather information from within the macro about all the declared variables to be able to store them. Later I also want to re-establish them when I continue execution. The await forms can be nested, of course :)
I had a peek into core.async source code because it is doing a similar thing inside, but what I have found there made me shiver - it seems they employ the Clojure AST analyser to get this info. Is this really so complex? I know of &env variable inside a macro, but do not have any idea how to use it in this situation. Any help would be appreciated.
And one more thing. Please do not ask me why I need this or that there is a different way of solving a problem - I want this specific solution.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果您忽略这一点,那么您可以处理的 Clojure 表达式类型将受到很大限制。函数无处不在,例如在
doseq
和for
的实现中。同样,许多有趣的程序将依赖于一些 Java 对象,例如文件句柄或其他对象。如果您设法编写这样的执行器,我怀疑它的实现无论如何都需要了解局部变量。因此,您可以推迟这个问题,直到完成执行程序的实现 - 如果您可以实现执行程序,您可能会发现它已经过时。
是的,这是非常侵入性的。您基本上是在编写一个编译器。感谢您的幸运之星,他们已经为您编写了分析器,而不必自己分析表达式。
这是最简单的部分。如果您愿意,您可以编写一个简单的宏来为您提供范围内的所有局部变量。这个问题之前已经被问过并回答过,例如在 Clojure get local let 中。
在提问时,这通常是一种没有成效的态度。它承认你提出了 XY 问题,但仍然拒绝告诉任何人 Y 是什么。
If you ignore this, it will be very restrictive for the kinds of Clojure expressions you can handle. Functions are everywhere, e.g. in the implementation of things like
doseq
andfor
. Likewise, a lot of interesting programs will depend on some Java object like a file handle or whatever.If you manage to write such an executor, I suspect its implementation will need to know about local variables anyway. So you can put off this question until you are done implementing your executor - you will probably find it obsolete, if you can implement your executor.
Yes, this is very intrusive. You are basically writing a compiler. Thank your lucky stars they wrote the analyzer for you already, instead of having to analyze expressions yourself.
This is the easy part. If you like, you can write a simple macro that gives you all the locals in scope. This question has been asked and answered before, e.g. in Clojure get local lets.
This is generally an unproductive attitude when asking a question. It's admitting you're posing an XY problem, and still refusing to tell anyone what the Y is.