実際のプロジェクトを使用してHaskellを学習しようとすると、次の定義に出会いました。私は各議論の前にある感嘆符が何を意味するのか理解しておらず、私の本はそれを言及していないようでした。
data MidiMessage = MidiMessage !Int !MidiMessage
これは厳密な宣言です。基本的には、データ構造の値が作成されるときに、いわゆる「弱い通常の頭形」に評価される必要があることを意味します。例を見てみましょう。これが意味するところを見ることができます。
_data Foo = Foo Int Int !Int !(Maybe Int)
f = Foo (2+2) (3+3) (4+4) (Just (5+5))
_
上記の関数f
は、評価されると、「サンク」、つまり値を計算するために実行するコードを返します。その時点では、Fooはまだ存在していません。コードだけです。
しかし、ある時点で、おそらくパターンマッチを介して、誰かが内部を調べようとする場合があります。
_case f of
Foo 0 _ _ _ -> "first arg is zero"
_ -> "first arge is something else"
_
これにより、必要なことを行うのに十分なコードが実行され、それ以上は実行されません。したがって、4つのパラメーターを持つFooを作成します(存在しないと内部を見ることができないため)。最初は、テストしているので、_4
_に至るまで評価する必要があります。
2番目は、テストしていないため、評価する必要はありません。したがって、_6
_をそのメモリの場所に保存するのではなく、後で評価できるようにコード_(3+3)
_を保存します。誰かがそれを見た場合にのみ6に変わります。
ただし、3番目のパラメーターの前には_!
_があるため、厳密に評価されます:_(4+4)
_が実行され、_8
_がそのメモリーの場所に格納されます。
4番目のパラメーターも厳密に評価されます。しかし、ここで少し注意が必要です。完全に評価するのではなく、弱い通常の頭部形状のみを評価しています。これは、それがNothing
なのかJust
なのかを把握し、それを保存することを意味しますが、それ以上先に進みません。つまり、_Just 10
_ではなく、実際にJust (5+5)
を格納し、内部のサンクを評価せずに残します。これは知っておくべき重要なことですが、これが意味することはすべてこの質問の範囲を超えていると思います。
BangPatterns
言語拡張機能を有効にすると、同じ方法で関数の引数に注釈を付けることができます。
_f x !y = x*y
_
f (1+1) (2+2)
は、サンク_(1+1)*4
_を返します。
厳密なコンストラクター引数と非厳密なコンストラクター引数の違いを確認する簡単な方法は、未定義の場合の動作です。与えられた
data Foo = Foo Int !Int
first (Foo x _) = x
second (Foo _ y) = y
厳密でない引数はsecond
によって評価されないため、undefined
を渡しても問題は発生しません。
> second (Foo undefined 1)
1
ただし、値を使用しない場合でも、厳密な引数をundefined
にすることはできません。
> first (Foo 1 undefined)
*** Exception: Prelude.undefined
これは厳密な注釈だと思います。
Haskellは純粋でlazy関数型言語ですが、レイジーネスのオーバーヘッドが大きすぎたり無駄だったりする場合があります。そのため、サンクを解析する代わりに、コンパイラーに関数の引数を完全に評価するよう依頼することができます。
このページには詳細情報があります: パフォーマンス/厳格さ 。