LoginSignup
14
6

More than 5 years have passed since last update.

Pattern Synonymsで遊ぶ

Last updated at Posted at 2017-09-28

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は可読性を損なう恐れがありますので、用法、用量を守って正しくお使い下さい。

14
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
6