2017-09-17 4 views
5

Dialyzerバージョン2.9。 Erts 7.3。 OTP次不自然Erlangのコードでは18機能がエクスポートされたときにダイアライザがガード違反を検出していない

-module(dialBug). 
-export([test/0]). 
%-export([f1/1]). % uncomment this line 

test() -> 
    f1(1). 

f1(X) when X > 5 -> 
    X*2. 

透析器は、上記のコード上で実行されると、それはコードがガードテスト(X> 5)のように動作しないことを警告しているが成功することはできません。

ただし、3行目のコメントを外してf1/1関数の透析器をエクスポートすると、警告が表示されなくなりました。

私はf1/1がエクスポートされるとき、外部クライアントがそれを使用できるので、ガード句が失敗することをダイアライザが知ることは不可能であることを認識しています。しかし、なぜtest/0がf1/1を間違って使用していると判断できなくなるのでしょうか?

答えて

7

透析器には、タイプチェッカーとしていくつかの制限があります。透析器は厳密なタイパーではなく、ルースタイパーです。これは、呼び出し元が何か悪いことをしている可能性があると推測する特定の場合とは対照的に、関数が宣言されている方法で明らかに間違っているものが見つかった場合にのみ警告を出すことを意味します。

サイトを呼び出すことを推測しようとしますが、基本的な型宣言で伝えることはできません。したがって、整数値はneg_integer()pos_integer()non_neg_integer()、または任意のinteger()と定義できますが、正当な値の境界を明確に定義していない限り、5..infinityから任意の範囲を定義する方法はありませんが、5..10のような範囲を定義し、期待した結果を得ることができます。

奇妙な点は、警備員がDialyszerに情報を提供している間に、許容される/ゆるいタイパーなので、コーリングサイトのエラーを検出できる十分な定義を持つ仕様機能のコーダーに大きな負担がかかります。ここで

は(完全にこのすべてを表示するには、私と一緒に長い画面のそのビットを負担するが、何もコードよりも優れ関連する問題を示していない)、これらのものは、実際のコード+透析器の出力に出て遊ぶ方法です:

オリジナル問題

-module(dial_bug1). 
-export([test/0]). 
%-export([f/1]). 

test() -> 
    f(1). 

f(X) when X > 5 -> 
    X * 2. 

透析日:我々はSでき、閉じた世界で

dial_bug1.erl:5: Function test/0 has no local return 
dial_bug1.erl:8: Function f/1 has no local return 
dial_bug1.erl:8: Guard test X::1 > 5 can never succeed 
done in 0m1.42s 
done (warnings were emitted) 

ので、限られたケースしかないため、Dialyzerは発信者に戻ってきます。

第二の変形

-module(dial_bug2). 
-export([test/0]). 
-export([f/1]). 

test() -> 
    f(1). 

f(X) when X > 5 -> 
    X * 2. 

ダイアライザーは言う:呼び出し側は誰が何を送信する可能性があり開放的な世界では、

done (passed successfully) 

、バックトラックと宣言されていないを確認するための努力はありません無制限の範囲

第三の変形

-module(dial_bug3). 
-export([test/0]). 
-export([f/1]). 

-spec test() -> integer(). 

test() -> 
    f(-1). 


-spec f(X) -> Result 
    when X  :: pos_integer(), 
     Result :: pos_integer(). 

f(X) when X > 5 -> 
    X * 2. 

ダイアライザーは言う:私たちは(この場合は、正の整数の集合)申告オープンレンジを持っているオープンワールド

dial_bug3.erl:7: Function test/0 has no local return 
dial_bug3.erl:8: The call dial_bug3:f(-1) breaks the contract (X) -> Result when X :: pos_integer(), Result :: pos_integer() 
done in 0m1.28s 
done (warnings were emitted) 

を不快な呼び出しサイトが見つかります。

第4の変形

-module(dial_bug4). 
-export([test/0]). 
-export([f/1]). 

-spec test() -> integer(). 

test() -> 
    f(1). 


-spec f(X) -> Result 
    when X  :: pos_integer(), 
     Result :: pos_integer(). 

f(X) when 5 =< X, X =< 10 -> 
    X * 2. 

ダイアライザーは言う:

我々はダイアライザーは再び見つけることができませんことを見つける守らが、まだ宣言されていない範囲を持っているオープンワールドで
done (passed successfully) 

不快な発呼者。私の意見では、Dialyzer がチェックタイプの警備員からヒントを受け取りますが、それは明らかにではありません。は、数値範囲のガードのヒントを受け取りません。

dial_bug5.erl:7: Function test/0 has no local return 
dial_bug5.erl:8: The call dial_bug5:f(1) breaks the contract (X) -> Result when X :: 5..10, Result :: pos_integer() 
done in 0m1.42s 
done (warnings were emitted) 

をそしてここで我々はspoon-場合ことを参照してください。だから、第5の変形

...私たちは有界が、任意の範囲を宣言した場合を見てみましょう

-module(dial_bug5). 
-export([test/0]). 
-export([f/1]). 

-spec test() -> integer(). 

test() -> 
    f(1). 


-spec f(X) -> Result 
    when X  :: 5..10, 
     Result :: pos_integer(). 

f(X) when 5 =< X, X =< 10 -> 
    X * 2. 

ダイアライザーは述べていますそれは期待どおりに仕事をします。

私は、これは「バグ」または「ダイアライザーの緩みの制約」と見なされているかどうか本当にわかりません。痛みのダイアライザーアドレスの主なポイントは、ネイティブ型を失敗し、ない数値範囲です。

言った...

を私が今まで現実の世界で有用で実際のプロジェクトで実際の作業のコードでこの問題を抱えているときにすべての - 私はすでに、私は有効なデータを扱っていますかどうかを事前によく知っていますかではない、と非常に少数の例では、私は、私はいつものいずれかにこれを記述しないでください!:

  1. クラッシュあからさま(が悪い結果を返さない決してだけではなく、死ぬこれが理由で私たちの宗教です。 )
  2. フォームの包まれた値を返します。発信者に何をするかを決定させます(これにより、すべてのケースでより良い情報が得られます)。

保護された例は関連しています。上記の最終的な例では、制限付きガードがクラッシュ可能な関数の適切なバージョンになります。

-spec f(X) -> Result 
    when X  :: 5..10, 
     Result :: {ok, pos_integer()} 
       | {error, out_of_bounds}. 

f(X) 5 =< X, X =< 10 -> 
    Value = X * 2, 
    {ok, Value}; 
f(_) -> 
    {error, out_of_bounds}. 
+0

は非常に徹底した答えをいただき、ありがとうございます。私はそれはバグではないが、それは一貫性がないように見える。ジョー・アームストロングの本には、透析器がnon_neg_integer()である(階級に輸出されているにもかかわらず)factorial(N)の型/範囲をどのように推論し、負の数で呼び出しのエラーを検出できるかが示されています。その面では、ガードの範囲を検出するよりもはるかに印象的な分析のように思える。 –

+1

@DavidJそうです。タイピング言語の基本型と一致していても、矛盾していると感じます。しかし、それは合理的に改善できると思う。私はDialyzerをレンジガードを仕様ヒントに変換させ、無制限の範囲を受け入れるようにtypespec言語を拡張します( '69 ..infinity'や' infinity..37.6'のような範囲を表現することは全く合理です)if指定された範囲はガードと競合します(ランタイムとプレコンパイルの時間なので、ガードは実質的に実行時の障害になります)。 – zxq9

+1

関連する質問:https://stackoverflow.com/questions/43307949/erlang-dialyzer-integer-ranges – aronisstav

関連する問題