5

Elixir の型と関数の仕様でパラメーター化された型型変数を組み合わせる方法を見つけようとしています。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それを強制するために関数の型仕様がどうあるべきかわかりません。Erlang では、次の構文でうまくいくと確信しています。

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

Elixir を使用してこの制約を表現する方法はあり@specますか?

4

1 に答える 1

8

(私は普通の Erlang の方が流暢ですが、コードは簡単に移植できるはずです。)

別の を書く場合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().

残念ながら、Erlang 18 の時点で、Dialyzer はこの仕様を最も厳密な意味で読み取っていません (すべてのインスタンスが単一化可能である必要Tがあります)。それぞれTterm().

したがって、Erlang でも Elixir でも警告は出されません。

Erlang での例の完全なコード:

-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.
于 2015-11-03T09:16:48.630 に答える