Skip to content

Commit 543c3ca

Browse files
committed
Complete the case of 'where clause'
1 parent 1fa0f30 commit 543c3ca

File tree

2 files changed

+56
-5
lines changed

2 files changed

+56
-5
lines changed

examples/2020/strict-gotchas/where.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
-- And
77
-- runghc --ghc-arg=-XStrict where.hs
88

9+
910
main :: IO ()
1011
main = print $ div10 0
1112

preprocessed-site/posts/2020/strict-gotchas.md

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
title: Strict拡張を使用する際の注意点
33
headingBackgroundImage: ../../img/background.png
44
headingDivClass: post-heading
5-
author: Yuji Yamamoto
6-
postedBy: <a href="http://the.igreque.info/">Yuji Yamamoto(@igrep)</a>
5+
author: YAMAMOTO Yuji
6+
postedBy: <a href="http://the.igreque.info/">YAMAMOTO Yuji(@igrep)</a>
77
date: January 26, 2020
88
tags:
99
...
@@ -19,7 +19,7 @@ Haskellは他の多くのプログラミング言語と異なった特徴を持
1919
これらの拡張を大雑把に言うと、
2020

2121
- `StrictData`: 値コンストラクターにおいて、引数の値が弱頭正規形(Weak Head Normal Form。以降慣習に従い「WHNF」と呼びます)まで評価されるようになる
22-
- `Strict`: 値コンストラクターを含めたあらゆる関数やローカル変数の定義において、パターンマッチで束縛した変数の値がWHNFまで評価されるようになる
22+
- `Strict`: 値コンストラクターを含めたあらゆる関数やローカル変数の定義において、パターンマッチで代入した変数の値がWHNFまで評価されるようになる
2323

2424
というものです。
2525

@@ -56,9 +56,59 @@ stack exec runghc -- <これから紹介するコードのファイル>.hs
5656

5757
なお、使用したGHCのバージョンは8.6.5で、OSはWindows 10 ver. 1909です。
5858

59-
# Case hoge: `where`句だろうとなんだろうと評価
59+
# Case 1: `where`句だろうとなんだろうと評価
6060

61-
hoge
61+
最初のケースは、遅延評価で当たり前に享受していたメリットが、`Strict`を有効にしている状態では得られなくなってしまう、というものです。
62+
[pfxfncさんのStrict拡張でハマったお話](https://qiita.com/pxfnc/items/a26bda6d11402daba675)という記事でも紹介されてはいますが、まとめ記事なのでここでも改めて取り上げます。
63+
64+
```haskell
65+
main :: IO ()
66+
main = print $ div10 0
67+
68+
div10 :: Int -> Int
69+
div10 n
70+
| n == 0 = 0
71+
| otherwise = result
72+
where
73+
result = 10 `div` n
74+
```
75+
76+
ご覧のとおり、本当にほとんどpfxfncさんの記事のサンプルそのままで恐縮ですが、このプログラム、👇のように`Strict`拡張を有効にして実行するとエラーが起こります。
77+
78+
```bash
79+
> stack exec -- runghc --ghc-arg=-XStrict where.hs
80+
where.hs: divide by zero
81+
```
82+
83+
一方、`Strict`拡張を有効にしなかった場合、エラーは起こりません。
84+
85+
```bash
86+
> stack exec -- runghc where.hs
87+
0
88+
```
89+
90+
なぜこんなことが起こるのでしょう?
91+
92+
これは、`Strict`拡張がパターンマッチで代入したあらゆる変数の値をWHNFまで評価するようになった結果、`where`句で代入した変数まで必ずWHNFまで評価してしまうために発生したエラーです。
93+
すなわち、`where`における、
94+
95+
```haskell
96+
result = 10 `div` n
97+
```
98+
99+
までもが、
100+
101+
```haskell
102+
!result = 10 `div` n
103+
```
104+
105+
とBangパターンを付けた代入であるかのように解釈されたのです[^bangpatterns]
106+
107+
[^bangpatterns]: `BangPatterns`言語拡張を有効にした上で上記のように書き換えてみると、`Strict`拡張の有無に関わらずエラーが発生します。試してみましょう。
108+
109+
こうなると、`result`を使用しないケース、すなわち`n == 0 = 0`の場合であっても`result`に <small>(WHNFまで評価した)</small>値を代入するのに必要な計算は実行され、結果<code>10 `div` 0</code>が計算されようとして`divide by zero`が発生するのです。
110+
111+
`where`句は関数定義の後ろの方に書くという性格上、見落としがちかも知れません。注意しましょう。
62112

63113
# Case hoge: ポイントフリースタイルかどうかで変わる!
64114

0 commit comments

Comments
 (0)