无法在Erlang中创建规范

无法在Erlang中创建规范,erlang,Erlang,我试图在Erlang模块中创建一个非常简单的规范,但我遇到了这个错误 未定义函数比较规范/2 这是我的密码: -module(spec_example). -spec compare(any(), any()) -> less | equal | greater. -record(heap_node, { item :: any(), children :: [#heap_node{}] }). -record(priority_queue,

我试图在Erlang模块中创建一个非常简单的规范,但我遇到了这个错误

未定义函数比较规范/2

这是我的密码:

-module(spec_example).
-spec compare(any(), any()) -> less | equal | greater.

-record(heap_node, { item :: any(),
                     children :: [#heap_node{}] }).

-record(priority_queue, { root :: #heap_node{} | nil,
                          comparer :: compare() }).
我不能在这里定义比较函数,因为它将作为外部参数提供。我在GitHub找到了,我想它们都很好用


我在一个模块和一个头文件中都试过,但错误是一样的。我一定是遗漏了一些非常基本的东西。

你举的例子是头文件(.hrl),而不是源文件(.erl),他们将其导入到应该以某种方式运行的文件顶部

这似乎是一种强制执行少数模块都以相同方式运行的方法。这让我很困惑,因为这种行为定义正是行为存在要处理的

另外,请注意,他们的
api.hrl
还导入了
wf.hrl
,然后这些都是。从我看到的代码来看,没有理由不将其定义为行为。(这可能是有原因的,但从代码中看并不明显……我想不出一个好的理由来避免在这种情况下使用行为。)

功能规格

定义函数规范时,需要有一个底层函数来对其进行注释。因此,这本身就是非法的:

-spec add(integer(), integer()) -> integer().
但这是合法的:

-spec add(integer(), integer()) -> integer().

add(A, B) ->
    A + B.
使用类型、规格、edoc等的示例:

定义行为

那么“什么是行为?”假设您希望在程序中有一个位置,在这里您可以动态地选择要调用的模块,并且您必须期望该模块的行为与其他类似定义的模块的行为相同。如果需要调用
foo:frob/2
,它的工作方式(在类型方面)应该与
bar:frob/2
完全相同。因此,我们可能会在
foo\u bar.erl
内部的某个地方编写一个行为定义,然后将模块
foo.erl
bar.erl
声明为
foo\u bar
类型的行为:

-module(foo_bar).

-callback frob(integer(), inet:socket()) -> ok.
然后后来

-module(foo).
-behavior(foo_bar).

frob(A, B) ->
    % ...
如果模块
foo
缺少指定类型的函数
frob/2
,则将抛出警告,因为它无法匹配其声明的行为

这对于简化检查、管理和保持模块间API兼容性的方式非常有用。另外,请注意,您可以在模块上定义任意数量的回调,并且可以在模块中声明任意数量的行为

见:

我不能在这里定义比较函数,因为它将作为外部参数提供

对于这种情况,您需要
-键入
,正如Dogbert在评论中提到的:

-type compare() :: fun((any(), any()) -> less | equal | greater).

-record(priority_queue, { root :: #heap_node{} | nil,
                          comparer :: compare() }).

因为您要描述的是值的类型,而不是模块中名为
compare
的函数的规范。

-spec
只能键入在当前模块中声明的顶级函数,而不能键入作为参数传递给顶级函数或存储在记录中的匿名函数。对于这些,您可以使用
-type
fun(X)->Y
语法:

-type compare() :: fun((any(), any()) -> less | equal | greater).

-record(heap_node, { item :: any(),
                     children :: [#heap_node{}] }).

-record(priority_queue, { root :: #heap_node{} | nil,
                          comparer :: compare() }).
如果我现在创建一个优先级_队列,其中比较器是一个算术函数:

main() ->
  #priority_queue{root = nil, comparer = fun(_) -> equal end}.
然后运行透析器,我得到:

Record construction #priority_queue{root::'nil',comparer::fun((_) -> 'equal')} violates the declared type of field comparer::fun((_,_) -> 'equal' | 'greater' | 'less')

你的意思是做
-type
而不是
-spec
?@Dogbert我对Erlang是新手。我不应该在函数中使用spec属性吗?谢谢@zxq9给出这个很好的答案。这使规格错误一目了然。您还可以建议我如何定义函数签名(接受2个参数并返回这3个值中的一个),以便在代码的其他部分重用它吗?这在Erlang代码中是可能的还是常见的?@UfukHacıoğullarıSpeccing是针对每个函数的,不可重用。代码中出现类似(但不完全相同)规范的地方是,在顶部的gen_服务器上有一个接口函数(比如说,这就形成了一个
gen_server:call/2
,而该调用的处理程序在内部的实现通常具有相似但不完全相同的规范。如果您有一个公共API,需要匹配多个模块,那么您应该定义一个适用于所有模块的行为。否则,同一规范在代码中多次重复出现就是一个标志你可能会重构。@UfukHacıoğulları这可能会成为一个相当长的讨论,mods会将其从评论中删除。如果你想通过一个示例或你发现重复规范的具体案例来讨论。类型定义正是我想要的。我也尝试过,但显然语法错误。然后我思想规范只适用于函数。感谢您和@zxq9提供了这些伟大的答案。