【发布时间】:2016-07-17 11:59:30
【问题描述】:
Elixir 的文档 states 那
除了 Elixir 文件扩展名 .ex,Elixir 还支持 用于编写脚本的 .exs 文件。 Elixir 对这两个文件的处理方式完全相同 方式,唯一的区别是意图。 .ex 文件的目的是 在 .exs 文件用于脚本时编译,无需 编译。
但我仍然不确定何时使用哪种文件类型。 .ex 和 .exs 的缺点和用途是什么?
【问题讨论】:
标签: elixir
Elixir 的文档 states 那
除了 Elixir 文件扩展名 .ex,Elixir 还支持 用于编写脚本的 .exs 文件。 Elixir 对这两个文件的处理方式完全相同 方式,唯一的区别是意图。 .ex 文件的目的是 在 .exs 文件用于脚本时编译,无需 编译。
但我仍然不确定何时使用哪种文件类型。 .ex 和 .exs 的缺点和用途是什么?
【问题讨论】:
标签: elixir
.ex 用于编译代码,.exs 用于解释代码。
例如,ExUnit 测试位于 .exs 文件中,因此您不必在每次更改测试时都重新编译。如果您正在编写脚本或测试,请使用 .exs 文件。否则,只需使用 .ex 文件并编译您的代码。
就优点/缺点而言,解释将需要更长的时间来执行(因为 elixir 必须解析、标记化等),但不需要编译即可运行。差不多就是这样 - 如果运行脚本的灵活性比优化的执行时间更重要,请使用.exs。大多数时候,你会使用.ex。
【讨论】:
Elixir 将编译整个 .ex 文件。
.exs 文件也会被编译,但在调用时会被执行。因此,.exs 文件的大多数用例是在调用时立即执行代码。考虑使用.exs 文件进行测试、迁移数据和运行脚本。将.ex 文件视为应用程序的主要业务逻辑。
考虑这个例子
.ex 示例
sum.ex:
defmodule Sum do
def add(a, b) do
a + b
end
end
$ iex sum.ex
...
Interactive Elixir (1.9.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Sum.add(1, 2)
3
.exs 示例
sum.exs:
defmodule Sum do
def add(a, b) do
a + b
end
end
#within same file
IO.puts "The sum of 3 + 2 is: #{inspect Sum.add(3, 2)}"
$ elixir sum.exs
The sum of 3 + 2 is: 5
【讨论】:
我在 Elixir 版本 1.9.1 中检查它,两个扩展 .ex、.exs 将使用 elixirc 编译。换句话说,我们在这两种情况下都得到了字节码(.beam 文件)。
【讨论】:
对于一些更具体的细节,以下是我注意到的关于何时使用 .ex 与 .exs 文件的一些模式或经验法则:
几乎所有代码都放在我的应用程序“库”目录树中的 .ex 文件中,例如在 lib 下(或者如果应用程序是旧的 Phoenix webapp,则可能是 web)。可以在应用程序的其他地方或从iex 调用代码(如果它定义了公共函数或宏)。这是默认设置。
在选择.ex 和.exs 时,最重要的考虑因素可能是我想在何处(以及何时)调用该代码:
.ex
iex(例如iex -S mix)-.ex,如果该代码也(或可能将)被应用程序使用; .exs 用于“临时”脚本,例如针对特定错误或其他问题mix – .ex 用于自定义 Mix 任务,或作为 mix.exs 中的数据(用于简单任务别名).exs 并通过 elixir some-script.exs 运行。令人惊讶的是,以 [2]、[3] 或 [4] 之一开头的一段好代码,最终还是以 [1] 结尾。
要记住的一件非常重要的事情是,从例如移动代码很容易。将.exs 文件转换为.ex 文件(反之亦然)。
对于 [2],特别是对于一个应用程序,我设置了一些额外的“脚本模式”混合环境以用于 iex。基本上,它们运行应用程序(部分)的基本“开发”本地实例,但连接到生产/暂存数据库(或其他后端服务)。我有一些用于这些“命令环境”的辅助函数/宏,但大多数情况下我只是使用应用程序代码,例如查询生产/登台数据库并修复/清理/检查一些数据。如果我确实需要做一些甚至有点“涉及”(复杂)的事情,我通常会创建一个临时的“问题脚本”。在我的编辑器中编写代码比在iex 中容易得多。
对于 [2] 的临时脚本,我在一个项目中有一个 dev/scripts 子目录树,并且大多只是根据相应的票号(来自该项目的票证系统)命名临时脚本,例如. 123.exs。然后,在iex 中,我可以轻松运行该脚本:
iex> c "dev/scripts/123.exs"
一个不错的“工作流程”是在脚本文件中定义一个(顶级)模块:
defmodule Ticket123 do
...
def do_some_one_off_thing ...
end
然后,来自iex:
iex> c "dev/scripts/123.exs"
[Ticket123]
iex> Ticket123.do_some_one_off_thing ...
Something something
...
对于 [3],您项目的 mix.exs 和“完全”自定义 Mix 任务中的任务别名都非常好。
来自一个项目的一些示例别名:
Ecto) 数据库迁移和一些我们想要“手动”运行的迁移(即使用上面提到的“脚本模式”环境)。我们对长时间运行的数据库查询/命令等事情使用手动迁移,例如。在大表上添加或修改索引。test – 这隐藏了内置 Mix 任务以执行一些自定义本地数据库设置todo – 在我们的 (Elixir) 代码中输出 TODO cmets 的搜索结果来自同一项目的一些示例自定义任务:
Mix.Tasks.OneProject.BootstrapServer – 一些自定义{配置管理/自动化基础设施部署}代码Mix.Tasks.OneProject.DbSetup – 从 Elixir 应用程序中提取一些配置数据,但大多只是将其传递给 shell 脚本Mix.Tasks.OneProject.ImportTranslations – 从第三方下载翻译和其他国际化数据Mix.Tasks.OneProject.RollingDeploy – 使用类似于“蓝绿”模型的自定义部署,但使用负载均衡器而不是两个完全独立的环境/实例我在任何项目中都没有使用过完全“常规”的 Elixir 脚本文件——通过elixir some-script.exs 调用。可能最大的原因是不创建自定义 Mix 任务或“问题脚本”,即自定义 Mix 任务有很好的文档记录,即通过 mix help 和 mix help some-custom-mix-task,我 真的 喜欢在 REPL (iex) 工作。
【讨论】: