Haskell Language
厳密さ
サーチ…
バンパターン
bang( !
)で注釈が付けられたパターンは、遅延の代わりに厳密に評価されます。
foo (!x, y) !z = [x, y, z]
この例では、リストを返す前にx
とz
両方がweak head normal formに評価されます。それは以下と同じです:
foo (x, y) z = x `seq` z `seq` [x, y, z]
Bangパターンは、Haskell 2010 BangPatterns
言語拡張を使用して有効にします。
標準フォーム
この例では、簡単な概要を示します。 通常のフォームと例の詳細な説明は、 この質問を参照してください。
通常のフォームの縮小
式の縮小された正規形(または文脈が明らかである場合には通常の形)は、与えられた式のすべての縮小可能な部分式を評価した結果です。 Haskellの非厳密な意味論(通常は怠惰と呼ばれる)のために、サブ表現はバインダーの下にある場合(つまり、ラムダ抽象化 - \x -> ..
)、還元できません。式の通常の形式は、存在する場合は一意であるという性質を持つ。
言い換えれば、部分表現を減らす順序(意味論的意味論の観点から)は関係ありません。しかし、パフォーマンスの良いHaskellプログラムを書くための鍵は、正しい表現が適切なタイミングで評価されること、つまり操作上のセマンティクスを理解することを保証することです。
その正規形そのもので発現が正常な形態にあると言われます。
いくつかの式、たとえばlet x = 1:x in x
は、通常の形を持ちませんが、まだ生産的です。無限の値が許される場合 、例の式はまだ値を持っています 。ここではリスト[1,1, ...]
です。 let y = 1+y in y
などの他の式は値を持たないか、その値はundefined
です。
弱い頭の普通形
RNFは式を完全に評価することに対応します。同様に、弱い頭部標準形(WHNF)は式の先頭を評価することに対応します。式のヘッドe
あれば十分に評価されe
アプリケーションであるCon e1 e2 .. en
およびCon
コンストラクタです。または抽象化\x -> e1
;または部分アプリケーションf e1 e2 .. en
部分アプリケーション手段、 f
以上かかるn
引数(又は等価的に、タイプe
関数型です)。いずれにしても、部分式e1..en
はe1..en
にある式e1..en
評価されるか評価されundefined
。 undefined
あっても可能です。
ハスケルの評価セマンティクスはWHNFの形で記述することができます。式e
を評価し、まずそれをWHNFに評価し、その後、すべての部分式を左から右に再帰的に評価します。
プリミティブseq
関数は、式をWHNFに評価するために使用されます。 seq xy
にdenotationally等しく、 y
(の値seq xy
正確でありy
)さらに、 y
がWHNFと評価されるとき、 x
はWHNFと評価される。式は、(- -XBangPatterns
拡張によって有効になる)bangパターンでWHNFに評価することもできます。その構文は次のとおりです。
f !x y = ...
f
が評価されたときにx
はWHNFに評価され、 y
は(必然的に)評価されません。 bangパターンはコンストラクタにも現れます。
data X = Con A !B C .. N
この場合、コンストラクタCon
はB
フィールドで厳密であると言われます。これは、コンストラクタが十分な(ここでは2つの)引数に適用されたときにB
フィールドがWHNFに評価されることを意味します。
レイジーパターン
(構文で示さレイジー、または反駁 、パターン~pat
)も、常にマッチした値を見ずに、一致するパターンです。これは、遅延パターンがボトム値にも一致することを意味します。しかし、反駁不能なパターンのサブパターンに束縛された変数のその後の使用は、マッチが成功しない限り、パターンマッチングを強制的に実行し、最下位に評価する。
次の関数は引数に怠惰です。
f1 :: Either e Int -> Int
f1 ~(Right 1) = 42
それで
λ» f1 (Right 1)
42
λ» f1 (Right 2)
42
λ» f1 (Left "foo")
42
λ» f1 (error "oops!")
42
λ» f1 "oops!"
*** type mismatch ***
次の関数は、遅延パターンで書かれていますが、実際には一致を強制するパターンの変数を使用しているため、 Left
引数では失敗します。
f2 :: Either e Int -> Int
f2 ~(Right x) = x + 1
λ» f2 (Right 1)
2
λ» f2 (Right 2)
3
λ» f2 (Right (error "oops!"))
*** Exception: oops!
λ» f2 (Left "foo")
*** Exception: lazypat.hs:5:1-21: Irrefutable pattern failed for pattern (Right x)
λ» f2 (error "oops!")
*** Exception: oops!
let
バインディングは、のように反論できないパターンを怠惰に動作しています:
act1 :: IO ()
act1 = do
ss <- readLn
let [s1, s2] = ss :: [String]
putStrLn "Done"
act2 :: IO ()
act2 = do
ss <- readLn
let [s1, s2] = ss
putStrLn s1
ここact1
中のに対し、文字列の任意のリストに解析した入力に動作しますact2
putStrLn s1
の値必要s1
のためのパターンマッチング強制的に[s1, s2]
、ので、それだけで正確に二つの文字列のリストに対して動作します。
λ» act1
> ["foo"]
Done
λ» act2
> ["foo"]
*** readIO: no parse ***
厳密なフィールド
data
宣言では、型の前にバング( !
)を付けると、フィールドは厳密なフィールドになります 。データコンストラクタが適用されると、これらのフィールドは弱いヘッドノーマルフォームに評価されるので、フィールドのデータは常に弱いヘッドノーマルフォームになることが保証されます。
厳密なフィールドは、レコード型と非レコード型の両方で使用できます。
data User = User
{ identifier :: !Int
, firstName :: !Text
, lastName :: !Text
}
data T = MkT !Int !Int