HaskellにおいてDRY原則に反した型設計の改善
Haskellで設計を行っている時に、どうしてもDRY原則に反し、煩雑になってしまうコードが有ります。
特に、型クラスを使っている時にこの問題が発生することがあります。
実際の状況はかなり複雑で書ききれないので、重要な部分だけを取り出して問題形式にしました。
どのようにすればこの問題を解決できるでしょうか?
また、根本的に問題の捉え方が誤っているなどがあればご指摘いただければ幸いです。
{-
以下の条件で実装をするものとする。
まず、データ型X, Y, Zがあり、データ型は頻繁に追加されることが予想される。
(つまり、拡張性を高く保っておく必要がある)
あなたは、標準出力から、一行目にデータ型の種類を指定され、二行目にパース可能なデータが与えられる。
そのデータをパースし、データに対してある動作B(サンプル実装ではfuncBとした)を行ったあとに、
所与の動作A(funcA)を実行する必要がある。
サンプル実装では、test関数にその処理をまとめたが、これは明らかにDRY原則に反している。
サンプル実装をどのように改善すればDRY原則を守ることができるか。
(サンプル実装の中身はすべて書き換え可、動作Bはundefinedでよい)
-}
module Problem where
----------------------------------------------------------------------
-- 条件
data X = X
data Y = Y
data Z = Z
class Parsable a where
parse :: String -> a
instance Parsable X where
parse = undefined
instance Parsable Y where
parse = undefined
instance Parsable Z where
parse = undefined
class ClassA a where
funcA :: a -> IO ()
instance ClassA X where
funcA = undefined
instance ClassA Y where
funcA = undefined
instance ClassA Z where
funcA = undefined
----------------------------------------------------------------------
-- サンプル実装
class ClassB a where
funcB :: a -> IO ()
instance ClassB X where
funcB = undefined
instance ClassB Y where
funcB = undefined
instance ClassB Z where
funcB = undefined
test = do
t <- getLine
d <- getLine
case t of
"X" -> do
let obj = parse d :: X
funcB obj
funcA obj
"Y" -> do
let obj = parse d :: Y
funcB obj
funcA obj
"Z" -> do
let obj = parse d :: Z
funcB obj
funcA obj