Pattern synonymsとは
pattern synonymsとはGHC7.8から導入されたGHC拡張で、読んで字のごとく、パターンマッチで使うパターンに別名を付ける事ができる機能である。
基本
pattern synonymsの宣言構文には、以下の種類がある。
pattern pat_lhs = pat
pattern pat_lhs <- pat
[where ...]
前者で定義されるのはbidirectional synonymsといって、パターンマッチにも使えるし、コンストラクタ的な関数としても使う事ができる。
{-# LANGUAGE PatternSynonyms #-}
pattern Cons x xs = x:xs
pattern Nil = []
main = do
let x = Nil -- コンストラクタ的用法
case x of {Cons _ _ -> undefined; Nil -> putStrLn "Nil!"} -- パターンマッチ
後者で定義されるのはunidirectional synonymsといって、パターンマッチにしか使えないが、逆変換が必要ない分だけ自由に書けるのがメリット。
{-# LANGUAGE PatternSynonyms #-}
pattern Head x <- x:_ -- _ を含む物は = では定義できない
main = do
case [] of {Head _ -> undefined; _ -> putStrLn "No head!"}
さらに、whereで逆変換を手書きしてやる事もできる。これをexplicitly bidirectional synonymsと呼ぶ。
{-# LANGUAGE PatternSynonyms #-}
pattern Head x <- x:_
where Head x = [x] -- コンストラクタとして使った場合の定義を手書き
main = do
let x = Head 5
case x of {Head y -> print y; _ -> putStrLn "No head!"} -- 5がprintされる
unidirectional synonymsで遊ぶ
せっかくunidirectionalなのだからガードなど書ければ面白い気がするのだが、これは構文エラーになる。残念。
{-# LANGUAGE PatternSynonyms #-}
pattern Even <- x | even x -- Syntax error
しかし、ViewPatternsを使えば同じ事ができる。
{-# LANGUAGE PatternSynonyms, ViewPatterns #-}
pattern Even <- (even -> True)
アズパターンを使えば更に夢が広がりそうなのだが、pattern synonymsにおいてアズパターンは制限されている。
{-# LANGUAGE PatternSynonyms, ViewPatterns #-}
pattern Even x <- x@(even -> True)
synonyms.hs:3:19:
Pattern synonym definition cannot contain as-patterns (@):
x@(even -> True)
多分bidirectionalの時にダメだからだろう。これもViewPatternsで回避可能。
{-# LANGUAGE PatternSynonyms, ViewPatterns #-}
import Control.Arrow ((&&&))
pattern Even x <- (id &&& even -> (x, True))
マッチした値に関数適用して返したい。でもこれは構文エラー。
{-# LANGUAGE PatternSynonyms, ViewPatterns #-}
import Control.Arrow ((&&&))
pattern TwiceOf (x `div` 2) <- (id &&& even -> (x, True)) -- Syntax error
こうすれば出来る。そう、ViewPatternsならね。
{-# LANGUAGE PatternSynonyms, ViewPatterns #-}
import Control.Arrow ((&&&))
pattern TwiceOf x <- ((`div` 2) &&& even -> (x, True))
結論
自分で書いておいて何だが、これはちょっとアクロバティックすぎる気がする。
{-# LANGUAGE PatternSynonyms, ViewPatterns #-}
import Control.Arrow ((&&&))
pattern TwiceOf x <- ((`div` 2) &&& even -> (x, True))
main = do
case 2 of {TwiceOf x -> print x; _ -> print "Odd number"}
元々、某GUIライブラリで「キャスト可能性の確認→ダウンキャスト」が安全に出来たらいいな、と考えてみた方法でした。
あまり元の情報を変えてしまうようなpattern synonymsは可読性を損なう恐れがありますので、用法、用量を守って正しくお使い下さい。