PatternSynonymsは、その名の通り、パターンの別名である。GHC 7.8.1 で導入された。GHC 7系のPatternSynonymsは、モジュール内に閉じて入れば何の問題もなかったが、モジュールの外へexportする際は、patternキーワードが必要であり、構成子らしくなかった。
{-# LANGUAGE PatternSynonyms #-} module A (Foo, pattern Zero) where newtype Foo = Foo Int pattern Zero :: Foo pattern Zero = Foo 0
GHC 8 からは、patternキーワードが不要となり、構成子らしくなった。
{-# LANGUAGE PatternSynonyms #-} module A (Foo(Zero)) where newtype Foo = Foo Int pattern Zero :: Foo pattern Zero = Foo 0
PatternSynonymsの使いどころ
(ビット幅が決まっている)数値が何らかの意味を持つような問題を考える。たとえば、
0 | A |
1 | B |
2 | C |
その他 | 予約 |
のような対応があった場合、Haskell では以下のようなコードを書くことが多いだろう。
data Foo = A | B | C deriving (Show,Eq,Ord,Enum,Bounded)
簡潔でいいのだが、このコードには以下のような問題がある。
- 「その他」の数値が発生しうる場合にはどうするのか?
以下のように構成子を増やすと、Enum を導出できなくなる。
data Foo = A | B | C | Other Int deriving (Show,Eq,Ord)
他にも問題がある。
- 値が0から始まらない場合どうするのか?
- 値が連続してない場合どうするのか?
という訳で、この方法は筋が悪い。そこで、PatternSynonymsの登場である。
module A (Foo(A,B,C),fromFoo,toFoo) where newtype Foo = Foo { fromFoo :: Int } deriving (Eq) toFoo :: Int -> Foo toFoo = Foo pattern A :: Foo pattern A = Foo 0 pattern B :: Foo pattern B = Foo 1 pattern C :: Foo pattern C = Foo 2 instance Show Foo where show A = "A" show B = "B" show C = "C" show x = "Foo " ++ (show $ fromFoo x)
少しコード量は増えるが、問題に柔軟に対応できるのでストレスがない。