2015-11-03 42 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. 
:用於在二郎示例

完整代碼