data Animal1 = Dog1 String | Cat1 String Int call1 :: Animal1 -> String call1 (Dog1 name) = name call1 (Cat1 name whiskers) = name ++ " with " ++ show whiskers ++ " whiskers" test1 = mapM_ (putStrLn . call1) [Dog1 "pochi", Cat1 "tama" 12]
簡単だけれど、種類を増やす場合にはコンストラクタを増やした上で、パターンマッチの部分も増やさなくてはいけません。種類がおそらく増えないであろう場合、または変更が局所的であるほうがうれしい場合には良いかもしれません。
data Dog2 = Dog2 String callDog :: Dog2 -> String callDog (Dog2 name) = name data Cat2 = Cat2 String Int callCat :: Cat2 -> String callCat (Cat2 name whiskers) = name ++ " with " ++ show whiskers ++ " whiskers" data Animal2 = Animal2 { call2 :: String } dog2 :: Dog2 -> Animal2 dog2 dog = Animal2 { call2 = callDog dog } cat2 :: Cat2 -> Animal2 cat2 cat = Animal2 { call2 = callCat cat } test2 = mapM_ (putStrLn . call2) [dog2 (Dog2 "pochi"), cat2 (Cat2 "tama" 12)]
種類を増やしても既存のコードを変更する必要がありませんが、変換用の関数を書くのが面倒かもしれません。
data Dog3 = Dog3 String data Cat3 = Cat3 String Int class Animal3Class a where call3' :: a -> String instance Animal3Class Dog3 where call3' (Dog3 name) = name instance Animal3Class Cat3 where call3' (Cat3 name whiskers) = name ++ " with " ++ show whiskers ++ " whiskers" data Animal3 where Animal3 :: (Animal3Class a => a -> Animal3) call3 :: Animal3 -> String call3 (Animal3 a) = call3' a test3 = mapM_ (putStrLn . call3) [Animal3 (Dog3 "pochi"), Animal3 (Cat3 "tama" 12)]
2番目の方法と基本的には同じ(型の変換を型システムが勝手にやってくれると考えれば良い?)だと思いますが、C++やJavaのようなオブジェクト指向な考え方をすると最も普通に考えられるかも知れません。
data Animal3 where Animal3 :: (Animal3Class a => a -> Animal3)
のところは、Existentially quantified typesを使って以下のようにも書けます。
data Animal3 = forall a. Animal3Class a => Animal3 a