为什么在另一个不存在或具有错误arity的模块中调用函数时,没有编译时错误或警告?
编译器具有模块中的所有导出信息,以实现此目的。它还没有实现,还是有技术上的原因导致我无法看到它?
答案 0 :(得分:5)
我不知道它为什么会丢失(可能是因为模块是完全独立的,并且编译的一个并不真正依赖于另一个 - 但这只是推测)。但我相信你可以通过透析器静态分析找到这样的问题。看看http://www.erlang.org/doc/man/dialyzer.html
它是系统本身的一部分,因此请尝试将其包含在您的工作流程中。
答案 1 :(得分:4)
正如其他人所说的那样。模块是单独编译的,并且绝对不能保证编译时存在的环境与在运行时退出的环境相同。这意味着在编译时对模块或其中的函数的存在进行检查基本上没有意义。在运行时,模块可能加载也可能不加载,您调用的函数可能会或可能不会在模块中定义,或者它可能会执行与您预期完全不同的操作。
所有这一切都归功于Erlang系统的动态特性。没有真正的方法可以在运行时定义系统中的内容。热代码加载是其中的一部分,并且由于系统的动态特性而正常工作。这意味着您可以在运行时重新定义系统,可以加载具有不同界面的现有模块的新版本,并且可以加载在全新模块中删除现有模块。
为此,所有关于模块或函数存在的检查必须在运行时完成。
dialyzer 这样的工具可以帮助解决这个问题,但他们确实假设您在运行时没有做任何“搞笑”,而且您检查的系统是相同的作为你运行的系统。这当然都很好,但非常静态。并且反对Erlang的本性,即一切都是动态的。
不幸的是,在这种情况下,你不能同时吃蛋糕并吃掉它。
答案 2 :(得分:1)
这是由于热代码加载。每个模块都可以在任何特定时间加载。因此,当你的模块A
代码调用函数B:F
时,如果你的模块B
的源代码没有函数{{1},你就无法在编译时告诉它是错的。 }}。想象一下:您通过调用B:F
来编译模块A
。您将模块B:F
加载到没有函数B
的内存中。然后加载包含调用B:F
的模块A
,但不要调用它。然后使用B:F
编译模块B
的新版本。然后加载这个新模块然后你可以调用B:F
,一切都是完全正确的。想象一下,您的模块B:F
使模块A
即时启动并加载它。您无法在任何特定时间告诉模块B
包含对不存在的函数A
的调用是错误的。
答案 3 :(得分:1)
在我看来,大多数(如果不是全部)编译器都不会验证编译时是否存在函数。通常需要的是函数的原型声明:返回值的类型,所有参数的列表和类型。这是通过在每个模块定义(而不是.c或.cpp)中包含some_file.h在C / C ++中完成的。
在Erlang中,这种类型验证是在程序运行时动态完成的,因此不必包含这些定义。它甚至完全没用,因为Erlang允许在运行中升级应用程序,因此在应用程序生命周期内,功能类型可能会改变,或者功能可能会故意或错误地消失;这就是为什么Erlang设计师选择在运行时而不是在构建时进行验证的原因。
您所说的错误通常发生在代码生成的链接阶段,当“编译器”尝试将所有目标代码集合在一起以构建可执行文件或库时,链接器在此阶段解决所有外部地址(对于共享变量,静态调用......)。这个阶段在Erlang中不存在,一个模块是完全独立的;它不与应用程序的其余部分共享任何内容,没有变量或函数地址。
当然,在更新正在运行的生产程序之前必须使用一些工具并进行一些测试,但我认为这些验证的重要性与算法本身的正确性完全相同。
答案 4 :(得分:1)
编译例如调用alpha
的模块beta:some_function(...)
,编译器不能假设某些特定版本的beta
在运行时使用。在编译beta
之后,您可能会编译较新版本的alpha
,这将导出正确的some_function
。也许你会上传alpha
以在不同的主机上使用,主机上有所有其他模块。
编译器因此只编译远程调用,并且在运行时解决任何错误(不存在的模块或函数),此时将加载某个版本的beta
。
答案 5 :(得分:1)
您可以使用xref
应用程序检查已弃用,未定义和未使用的函数的使用情况(以及更多!)。
使用debug_info
编译模块:
Eshell V6.2 (abort with ^G)
1> c(test, debug_info).
{ok,test}
使用xref:m/1
检查模块:
2> xref:m(test).
[{deprecated,[]},
{undefined,[{{test,start,0},{erlang,foo,0}}]},
{unused,[]}]
您可以在此处查看有关xref
的更多信息:
Erlang -- Xref - The Cross Reference Tool (Tools User's Guide)