@@ -21,7 +21,7 @@ Haskellは他の多くのプログラミング言語と異なった特徴を備
2121
2222このうち、` StrictData ` は比較的リスクが少なく大変有用<small >(もはや標準であって欲しいぐらい)</small >という声をよく聞きますが[ ^ strictdata-sample ] 、` Strict ` については様々な問題点があることが知られています。今回はその各種問題点をまとめて共有することで、思い切って` Strict ` を有効にするときに参考になる情報を提供したいと思います!
2323
24- [ ^ strictdata-sample ] : 例えばfumievalさんによる[ この記事] ( http://fumieval.hatenablog.com/entry/2015/12/10/200630 ) より:> もっとも、日常ではここまで気にしなければいけない場面は少ないので、ほとんどの場合は気にせず感嘆符をつけて大丈夫だろう。GHC 8.0からは、全フィールドをデフォルトで正格にする` StrictData ` という拡張が入るため、こちらを使おう。
24+ [ ^ strictdata-sample ] : 例えばfumievalさんによる[ この記事] ( http://fumieval.hatenablog.com/entry/2015/12/10/200630 ) より: 「 もっとも、日常ではここまで気にしなければいけない場面は少ないので、ほとんどの場合は気にせず感嘆符をつけて大丈夫だろう。GHC 8.0からは、全フィールドをデフォルトで正格にする` StrictData ` という拡張が入るため、こちらを使おう」
2525
2626# 前提知識とその参考資料
2727
@@ -47,7 +47,7 @@ cd blog/examples/2020/strict-gotchas
4747stack exec runghc -- <これから紹介するコードのファイル>.hs
4848```
4949
50- 実際に試すときは` -XStrict ` というオプションを` runghc ` に付けた場合と付けなかった場合両方で実行して、違いを確かめてみてください。
50+ 実際に試すときは` --ghc-arg=- XStrict ` というオプションを` runghc ` に付けた場合と付けなかった場合両方で実行して、違いを確かめてみてください。
5151
5252なお、使用したGHCのバージョンは8.10.1で、OSはWindows 10 ver. 1909です。
5353
@@ -188,7 +188,7 @@ f = map (+ 3) . filter (> 2)
188188
189189しかし、` Strict ` を有効にしたモジュールでこのような書き換えを行うと、` f ` の挙動が変わってしまいます。引数` . ` を使って書き換える前は、引数` xs ` に言及していたところ` . ` を使って引数` xs ` に言及しなくなったからです。` filter ` も` map ` も` Strict ` 拡張を有効にしたモジュールで定義されているわけではないので、引数を正格に評価しないんですね。結果、こうした書き換えによって、** ` Strict ` 拡張を有効にしていても意図せず遅延評価してしまう** 、というリスクがあるので、リファクタリングの際はくれぐれも気をつけてください[ ^ list ] 。ざっくりまとめると、` Strict ` 拡張を有効にしているモジュールでは、「引数や変数を宣言することすなわちWHNFまで評価すること」、あるいは「引数や変数を宣言しなければ、評価されない」と意識しましょう。
190190
191- [ ^ list ] : もっとも、この場合引数はリストでしょうから 、WHNFまでのみ正格評価するメリットは少なそうですが。
191+ [ ^ list ] : もっとも、この例では引数はリストでしょうから 、WHNFまでのみ正格評価するメリットは少なそうですが。
192192
193193ちなみに、` referArgs ` における` _ ` のように「` Strict ` 拡張を有効にした場合さえ、使用していない引数が評価されてしまうのは困る!」という場合は、引数名の前にチルダ` ~ ` を付けてください。
194194
@@ -199,40 +199,41 @@ referArgs x ~_ = x
199199
200200# Case 3: 内側のパターンはやっぱりダメ
201201
202- サンプル: [ tuple.hs ] ( https://github.com/haskell-jp/blog/blob/master/examples/2020/strict-gotchas/tuple.hs )
202+ サンプル: 今回はGHCiですべて紹介するのでサンプルはありません。
203203
204204続いては、` Strict ` 拡張のドキュメントでも触れられている、入れ子になったパターンマッチにおける問題を紹介します。一言で言うと、` let (a, b) = ... ` のような、データ構造<small >(この場合タプルですね)</small >の「内側」に対するパターンマッチは、` Strict ` 拡張を有効にしていても正格に評価しないよ、という話です。
205205
206- 例えば、下記のコードを` Strict ` 拡張付きで実行しても、パターンマッチしている` a ` ・` b ` ともに代入した時点では正格評価されず、` error "a" ` ・` error "b" ` による例外はいずれも発生しません。
206+ 例えば、下記のコードを` Strict ` 拡張付きで実行しても、パターンマッチしている` a ` ・` b ` ともに代入した時点では正格評価されず、` error "a" ` ・` error "b" ` による例外はいずれも発生しません。次のコードをGHCiで試してみてください。
207207
208208``` haskell
209- (a, b) = (error " a" , error " b" )
209+ > : set - XStrict
210+ > (a, b) = (error " a" , error " b" )
211+ -- 何も起きない
210212```
211213
212- これは、タプルの値コンストラクター ` (,) ` が< small >( ` StrictData ` やStrictness flagを用いないで定義されているので)</ small >各要素を遅延評価することとは ** 関係なく ** 、各要素を正格評価する値コンストラクターであっても同様です 。
214+ 先ほどの節における「 ` Strict ` 拡張を有効にしているモジュールでは、『引数や変数を宣言することすなわちWHNFまで評価すること」』、あるいは『引数や変数を宣言しなければ、評価されない』と意識しましょう」という主張を真に受けてしまうと、意図せず遅延評価させてしまい、ハマりそうです😰。⚠️繰り返しますが「 ** 内側のパターンにおける変数は正格評価されない ** 」ということも意識してください 。
213215
214- ``` haskell
215- data MyTuple a b = MyTuple ! a ! b deriving Show
216+ 一方、` StrictData ` やStrictness flagを用いるなどして、各要素を正格評価するよう定義した値コンストラクターでは、ちゃんと評価して例外を発生させます。
216217
217- MyTuple a b = MyTuple (error " a" ) (error " b" )
218+ ``` haskell
219+ > : set - XStrict
220+ > data MyTuple a b = MyTuple a b deriving Show
221+ > let MyTuple a b = MyTuple (error " a" ) (error " b" )
222+ *** Exception : b
223+ CallStack (from HasCallStack ):
224+ error , called at < interactive>: 10 : 40 in interactive: Ghci7
218225```
219226
220- 先ほどの節における「 ` Strict ` 拡張を有効にしているモジュールでは、『引数や変数を宣言することすなわちWHNFまで評価すること」』、あるいは『引数や変数を宣言しなければ、評価されない』と意識しましょう」という主張を真に受けてしまうと意図せず遅延評価させてしまい、ハマりそうです😰。⚠️繰り返しますが「 ** 内側のパターンにおける変数は正格評価されない ** 」ということも意識してください 。
227+ ` Strict ` 拡張を有効にすると ` StrictData ` も自動的に有効になるので、👆における ` MyTuple ` 値コンストラクターは各要素を正格評価するようになったわけです。なので ` Strict ` 拡張を有効にしたモジュールにおいて、なおかつそこで定義した型で完結している限りは平和でしょう 。
221228
222- 以上のことを試していただくために、サンプルコードでは ` Strict ` を有効にしてもしなくても、結果が変わらないサンプルを用意しました< small >(コードは👆の例に ` main ` がくっついたのとあまり変わらないので解説は省きます)</ small >。以下、実行例を 。
229+ ただし、GHCiで試す場合に特に注意していただきたいのですが、GHCiで ** ` let ` をつけないでパターンマッチした場合は正格評価されない ** 、という点に注意してください。 ` let ` をつけないとトップレベルでの定義と見なされるからです。 [ Strict拡張のドキュメント ] ( https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#strict-by-default-pattern-bindings ) にも、「Top level bindings are unaffected by ` Strict ` 」とありますとおり、トップレベルでの定義は例外扱いされているのです 。
223230
224- ``` bash
225- > stack exec runhaskell ./tuple.hs
226- " Default tuple"
227- " Default tuple in MyTuple1"
228- " Other value in MyTuple1"
229-
230- > stack exec -- runghc --ghc-arg=-XStrict ./tuple.hs
231- " Default tuple"
232- " Default tuple in MyTuple1"
233- " Other value in MyTuple1"
231+ ``` haskell
232+ > : set - XStrict
233+ > data MyTuple a b = MyTuple a b deriving Show
234+ > MyTuple a b = MyTuple (error " a" ) (error " b" )
235+ -- 何も起きない
234236```
235-
236237# Case 4: ` foldr ` に渡す関数
237238
238239サンプル: [ stackoverflow-foldr.hs] ( https://github.com/haskell-jp/blog/blob/master/examples/2020/strict-gotchas/stackoverflow-foldr.hs )
0 commit comments