次のバージョンである GHC 8.0 では、正格評価用のプラグマして Strict と StrictData が提供されます。環境を作ってみたので、少し試しました。
以下のファイルを用意します。
{-# LANGUAGE DeriveFunctor #-} module A where data List a = Nil | Cons a (List a) deriving (Eq,Functor) xs :: List Int xs = Cons 1 (Cons 2 Nil)
注意:
- トップレベルの束縛とローカルの束縛では挙動が違います。Strict が効くのは、ローカルの束縛です。なので、GHCi で let を使って試します。
- 評価が遅延しているかは、GHCi の :sprint コマンドで確かめられます。GHC 7.8 以降の GHCi では MonomorphismRestriction が無効になっており、これが :sprint の動作に悪影響を与えますので、型が特定されるように型注釈を補うことを忘れないで下さい。(たとえば、上記 xs。)
いままで通り
% ghci A.hs > let ys = (+1) <$> xs > :sprint ys ys = _ > ys `seq` () () > :sprint ys ys = Cons _ _
関数適用も遅延するし、評価してもリストの構造が遅延していることが分かります。
StrictData
% ghci -XStrictData A.hs > let ys = (+1) <$> xs > :sprint ys ys = _ > ys `seq` () () > :sprint ys ys = Cons 2 (Cons 3 Nil)
リスト構造の遅延がなくなり、正格データとなりました。つまり、以下と同じ扱いです。
data List a = Nil | Cons !a !(List a) deriving (Eq,Functor)
Strict
% ghci -XStrict A.hs > let ys = (+1) <$> xs > :sprint ys ys = Cons _ _ > ys `seq` () () > :sprint ys ys = Cons _ _
ローカルの変数束縛は遅延していません。リストの構造は遅延しています。つまり、以下のように BangPatterns で書くのと同じです。
> let !ys = (+1) <$> xs
StrictData & Strict
% ghci -XStrictData -XStrict A.hs > let ys = (+1) <$> xs > :sprint ys ys = Cons 2 (Cons 3 Nil)
あー、もう、正格評価の言語みたい。:-) Haskell で正格評価のプログラミングをしたければ、こんな感じになります。
{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE Strict #-} {-# LANGUAGE StrictData #-} module A where data List a = Nil | Cons a (List a) deriving (Eq,Functor) xs :: List Int xs = Cons 1 (Cons 2 Nil)
おまけ
Strict プラグマの環境下で遅延束縛したいなら、"~" を使います。
> let ~ys = (+1) <$> xs