where 句は式でないため、print (x where x = 23 :: Int) のような書き方はできません (parse error on input ‘where’ が発生します) 。
本記事では、where 句の書き方についてまとめます。
1. where 句を書ける場所
モジュール定義、クラス宣言およびインスタンス宣言を除くと、where 句を書ける場所は以下の 2 種類のみです:
- 関数定義を含む変数束縛
- すなわち
whereの左側に=が必須
- すなわち
-
case式の選択肢- すなわち
whereの左側に->が必須
- すなわち
ちなみに無名関数では変数束縛しないため (\x -> x * y where y = 23 :: Int) のような書き方はできません。
参考「4.4.3 Function and Pattern Bindings - 4 Declarations and Bindings」
参考「3.13 Case Expressions - 3 Expressions」
参考「10 Syntax Reference」
2. where 句の書き方
2.1. 基本
f :: Int -> Int
f x = y
where
y = x * 42
main :: IO ()
main = do
print $ f 23
data T = Foo | Bar
main :: IO ()
main = do
let x = Foo :: T
print $ case x of
Foo -> y where y = 23 :: Int
Bar -> y where y = 42 :: Int
2.2. ガードを用いる場合
複数のガードを付けている場合でも、1 つの変数束縛および case 式の 1 つの選択肢に対して 1 つの where を使用することができます。
f :: Int -> Int
f x
| x >= 0 = y
| otherwise = - y
where
y = x * 42
main :: IO ()
main = do
print $ f 23
case 式の選択肢の場合でも変数束縛の場合と同様ですが、case 式内でガードを用いるとコードがややこしくなるため、そもそも関数を分ける等して case 式内でガードを用いない方が良いかもしれません。
main :: IO ()
main = do
let x = 23 :: Int
print $ case x of
y
| y >= 0 -> z
| otherwise -> - z
where
z = y * 42 :: Int
2.3. do 式内で where 句を書く場合
do 式に続けて where を書くと do 式内でなく do 式外に書くのと同じ意味になります。
main :: IO ()
main = do
print x
where
x = 23 :: Int
main :: IO ()
main = f
where
f = do
print x
x = 23 :: Int
do 式内の let による変数束縛で where 句を使用することで、do 式内で where 句を書くことができます。
main :: IO ()
main = do
let x = y * 42
where
y = 23 :: Int
print x
2.4. どうしても式内で where 句を書きたい場合
case () of _ -> で始まる case 式を用いることで、式内で where 句を書くことができます。
main :: IO ()
main = do
print (case () of _ -> x where x = 23 :: Int)
print
$ (\x -> case () of _ -> x * y where y = 42 :: Int)
(23 :: Int)