あどけない話

Internet technologies

PatternSynonymsのススメ

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)

少しコード量は増えるが、問題に柔軟に対応できるのでストレスがない。