あどけない話

Internet technologies

Strict Haskell

次のバージョンである 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