2015-11-03 43 views
3

我试图找出如何结合参数化类型类型变量在药剂类型和功能规格。举一个简单的例子,假设我定义一个Stack模块:药剂型规格和参数化类型变量

defmodule Stack do 
    @type t :: t(any) 
    @type t(value) :: list(value) 

    @spec new() :: Stack.t 
    def new() do 
    [] 
    end 

    # What should the spec be? 
    def push(stack, item) do 
    [item|stack] 
    end 
end 

第3行使用参数化类型规范,我可以定义,创建一个新的堆栈应该只包含整数的函数:

@spec new_int_stack() :: Stack.t(integer) 
def new_int_stack(), do: Stack.new 

到目前为止,这么好。现在我想确保只有整数可以被推入这个堆栈。例如,透析器应罚款与此:

int_stack = new_int_stack() 
Stack.push(int_stack, 42) 

但透析器应该抱怨这一点:

int_stack = new_int_stack() 
Stack.push(int_stack, :boom) 

我想不通的push功能的型号规格应执行什么。在二郎,我敢肯定,这句法会做的伎俩:

-spec push(Stack, Value) -> Stack when Stack :: Stack.t(Value). 

有表达使用药剂@spec此约束的方法吗?

回答

3

(我是纯二郎更流畅,但代码应该很容易端口)

如果你写一个单独的int_push/2(就像你做了new_int_stack/0),那么你当然可以写的:

-spec int_push(integer(), stack(integer())) -> stack(integer()). 

这应该允许透析器检测到滥用行为,纯粹是由于Item参数被指定为integer()

通用规范可以得到最接近的是这样的:

-spec push(T, stack(T)) -> stack(T) when T :: term(). 

不幸的是,二郎18,透析器不能在最严格意义上的(要求所有实例T是unifiable)阅读本规范。它只是要求每个Tterm()

因此,Erlang或Elixir都不会发出警告。

-module(stack). 

-export([new/0, new_int_stack/0, push/2, test/0]). 

-type stack() :: stack(any()). 
-type stack(T) :: list(T). 

-spec new() -> stack(). 

new() -> 
    []. 

-spec push(T, stack(T)) -> stack(T) when T :: term(). 

push(Item, Stack) -> 
    [Item|Stack]. 

-spec new_int_stack() -> stack(integer()). 

new_int_stack() -> 
    new(). 

-spec test() -> ok. 

test() -> 
    A = new(), 
    B = new_int_stack(), 
    push(foo, B), 
    ok. 
:用于在二郎示例

完整代码