あどけない話

Internet technologies

Haskellの文法(分岐編)

僕が Haskell を学び始めた頃、Haskell の文法はすんなりとは頭に入ってきませんでした。もともと僕はプログラミング言語の学習能力が低いので、僕だけかもしれませんが、「はじめからこう言ってもらえれば分かったのにぃ」ということを書きます。

はじめの一歩

分岐は case で書きます。以下に Maybe a に対する例を書きます。

case mx of
  Just x  -> ...
  Nothing -> ...

念のため、Maybe a の定義も見てみましょう。

data Maybe a = Nothing | Just a

列挙されているデータ構成子を case に列挙できることが分かるでしょう。このように、case でマッチできるのは、データ構成子で表現されたパターンになります。

ワイルドカード

たとえば、以下のような型を定義したとします。

data Foo = A | B | C | D

これを case でマッチさせると以下のようになります。

case x of
  A -> ...
  B -> ... -- ここと
  C -> ...
  D -> ... -- ここが同じ!

たとえば、B と D が同じ処理であれば、ワイルドカードでまとめることができます。

case x of
  A -> ...
  C -> ...
  _ -> ... -- まとめた

絶対に起こりえないパターンは、ワイルドカードで書いて、error にするようにしておきましょう。

case x of
  A -> ...
  C -> ...
  _ -> error "絶対に起こらないからエラーを書いても大丈夫"

if は case

if は case を用いて定義されています。if x then y else z は、以下のように変換されます。

case x of
  True -> y
  False -> z

関数のトップレベル分岐も case

関数のトップレベル分岐も case の構文糖衣です。たとえば、

foo (Just x) = ...
foo Nothing  = ...

は、

foo x = case x of
  Just x  -> ...
  Nothing -> ...

に変換されます。"=" が "->" に変換されることに注意して下さいね。

ガード

case の各パターンマッチには、ガードを書くことでさらに分岐させられます。ガードには真理値を書きます。

case mx of
  Just x
    | x > 100   -> ...
    | x < -100  -> ...
    | otherwise -> ...
  Nothing       -> ...

ちなみに、otherwise は True の別名です。

再び関数

case にガードが書けるのですから、関数のトップレベル分岐にもガードが書けます。

foo (Just x)
  | x >  100  = ...
  | x < -100  = ...
  | otherwise = ...
foo Nothing   = ...

繰り返しますが、"->" が "=" になることに注意。

let と where

let と where のスコープを直感的に示します。

foo a b c = let x = f + g
                y = f - g
            in x + y
  where
    f = a + b
    g = b + c
  • 関数の引数はどこからでも参照できる
  • let の中から where を参照できる
  • where の中から let は参照できない

お勧めの書き方

  • let はなるべく使わない。where を使う。
  • case はなるべく使わない。関数のトップレベル分岐を使う。
    • case を使いたい場合は、where の中に、トップレベル分岐を使ってローカル関数を定義すると読みやすくなるかも。

GHC のオプション

GHC のオプションには、必ず -Wall を付けましょう。(do を使うようになったら、 -fno-warn-unused-do-bind も付けましょう。) パターンが網羅できてなかったり、変数名がシャドーウィングを起こしていると、教えてくれます。警告はすべて消しましょう!