【问题标题】:Value from binding in LFE interpreter using Erlang使用 Erlang 在 LFE 解释器中绑定的值
【发布时间】:2015-06-04 06:34:21
【问题描述】:

我想使用 Lisp Flavored Erlang 作为 脚本扩展语言 用于 Erlang 应用程序。如果您愿意,可以使用类似的方式GNU Emacs通过Emacs Lisp配置和扩展。

我知道这个论点是广泛而有条理的;但在这个问题的特定情况下,我希望能够从 Erlang 代码中读取 LFE 中定义的 绑定名称(或变量,如果您愿意的话)。

我不是 LFE 内部架构的专家(这是软件工程和 Erlang 编程的一个很好的例子),但我在源代码和资源中都找不到答案在文档中。 查看sources,我可以看到LFE 包含一个针对Erlang VM编译器 和一个解释器。 后者是我正在尝试使用的。

如果我在 LFE 安装路径(在我的系统 $HOME/opt/lfe)中启动 Erlang shell/REPL


$ cd /path/to/LFE-install-dir
$ erl -pa ./ebin

我能够计算出一个值:


1> {ok, Expr} = lfe_io:read_string("(+ 1 10)").
{ok,['+',1,10]}
2> Result = lfe_eval:expr(Expr).
11

这是第一步,但不是我想要的。我宁愿绑定一个变量并读取它的值;那是我的问题:


3> {ok, Expr2} = lfe_io:read_string("(set a 10)").
{ok,[set,a,10]}
4> lfe_eval:expr(Expr2).
** exception error: {unbound_func,{set,2}}
     in function  lfe_eval:eval_expr/2

为什么set 被识别为未绑定函数?在 LFE REPL 中,这个表达式是有效的:

Erlang/OTP 17 [erts-6.4] [来源] [64 位] [smp:4:4] ... LFE Shell V6.4(使用 ^G 中止) >(设置一个10) 10 > 一个 10

我显然是以错误的方式使用 API。 我如何才能阅读 a 的内容和/或正确初始化 LFE 解释器? p>

(如果在某处有解释,请提供参考)。

【问题讨论】:

  • 原因是set是一个shell命令,它在shell之外是不存在的。通常在 LFE 代码中,您使用 letcase, lambda, match-lambda` 或其他有模式匹配的地方绑定变量。你能举一个更好的例子来说明你想如何使用它吗?
  • +1 感谢您的澄清。我正在学习各种 Lisp,发现 LFE 是 Erlang 生态系统中最有趣的原因。现在我正在尝试各种想法,但基本上我希望能够将该系统用作 Erlang 应用程序的配置 DSL。假设我的应用程序是一个服务器,我希望 connection timeout 在我的 config.lfe 中进行外部处理,那么最好的方法是什么?我应该定义一个get-conn-timeout 函数并读取其结果还是更好地从 Erlang 创建一个set_conn_timeout 并将其附加到为读取配置而创建的 LFE 会话中?
  • 由于这个问题在 LFE google 小组帖子 中被引用,我认为这对于那些对这些论点感兴趣的人可能会很有用,从这里链接回小组线程。 Lisp Flavored Erlang > LFE configuration script, LCFG and the likes.

标签: erlang lisp lfe


【解决方案1】:

我不会尝试完全回答您关于添加脚本的“最佳实践”的更广泛的问题。在我看来,在“基于钩子”的解决方案(您通过名称约定定义钩子实现并且它们被自动识别)和“显式 api”解决方案(您使用脚本环境中预定义的函数来注册您的钩子)之间进行选择或以其他方式调用配置函数)在很大程度上是一个品味问题。像 (set-connection-timeout-handler ...) 这样的显式调用可能更具可读性,更易于调试(没有拼写错误问题,api 更改没有意外),更容易记录,并且更灵活,但更多,好吧,明确的。

从您的简单变量定义示例构建,您可以通过以下几种方式开始进一步“解释”路径:

1> {ok, Expr} = lfe_io:read_string("'((a 10))").
{ok,[quote,[[a,10]]]}
2> lfe_eval:expr (Expr).
[[a,10]]

3> EvalAll = fun (Conf) -> {ok, E} = lfe_io:read_string("'(" ++ Conf ++ ")"), lfe_eval:expr(E) end.
#Fun<erl_eval.6.90072148>
4> EvalAll ("(a 10) (b 11)").                                                                      
[[a,10],[b,11]]

5> EvalAllL = fun (Conf) -> {ok, E} = lfe_io:read_string("(list " ++ Conf ++ ")"), lfe_eval:expr(E) end.
#Fun<erl_eval.6.90072148>
6> [{f, F}] = EvalAllL ("(tuple 'f (lambda (x) (+ 10 x)))").
[{f,#Fun<lfe_eval.12.2018457>}]
7> F (12).
22

8> G = fun (X) -> X * 2 end.
#Fun<erl_eval.6.90072148>
9> lfe_eval:expr (element (2, lfe_io:read_string ("(g 15)")), lfe_eval:add_lexical_func(g, 1, G, lfe_env:new ())).
30

【讨论】:

  • +1 这回答了我的问题,让我走上了正确的道路。在得到这个回复之前,我在推理你引用的两种脚本模式,最后可能最好的解决方案可能是从 LFE 返回一个数据结构(比如tuple)。你解释了这一点以及更多......回复真的帮助了我。在 LFE 文档或博客文章中对此进行解释会很棒。我想指出的一件很酷的事情是,这可以在 LFE 本身(或从 Elixir)中完成! Erlang 生态系统确实成熟且强大。
  • 您的回复、其他人以及 google 小组的讨论都由此诞生:github.com/gsscoder/cup
【解决方案2】:

一种简单的方法是使用“lfe”命令来运行 LFE 脚本。 'lfe' 的行为方式与 'bash' 类似,您可以使用它来运行脚本。 bin/lfec 中定义的“lfec”脚本就是一个很好的例子。它是一个 LFE shell 脚本,它解析它的参数并运行 LFE 编译器。

【讨论】:

  • 1+ 是的,bin/lfec 是一个使用lfe_comp:file/2 的有趣示例,如果我没记错的话,它会在后台编译脚本。 LFE 作为针对 Erlang VM 的 Lisp 环境非常棒,但我认为使用 CaaS(编译器即服务)模式公开自己是一件非常酷的事情。
  • @gsscoder 差不多,lfe_comp:file/2 编译作为lfec 参数的文件,脚本本身被解释。
  • @rivirding 你的意思是编译,因为它变成了BEAM的字节码?
  • @gsscoder 是的,它是一个独立的 LFE 编译器,可以将 .lfe 文件编译为 .beam 文件,就像 erlc 将 .erl 文件编译为 .beam 文件一样。
猜你喜欢
  • 2013-07-27
  • 1970-01-01
  • 2022-01-03
  • 1970-01-01
  • 1970-01-01
  • 2016-08-15
  • 2015-02-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多