数か月前にコンビナトリアル検索を行うために作成したコードを再検討しましたが、以前は型クラスで達成していたことを実行するための代替のより簡単な方法があることに気付きました。
具体的には、私は以前、探索問題の型の型クラスを持っていました。これには、型の状態、型s
のアクション(状態に対する操作)a
、初期状態、(アクション、状態)ペアのリストを取得する方法、および状態が解決策であるかどうかをテストする方法:
class Problem p s a where
initial :: p s a -> s
successor :: p s a -> s -> [(a,s)]
goaltest :: p s a -> s -> Bool
MultiParameterTypeClass拡張機能が必要であり、このクラスのインスタンスを作成する場合は通常、FlexibleInstancesと、場合によってはTypeSynonymInstancesが必要になるため、これはやや不十分です。また、関数のシグネチャが乱雑になります。
pathToSolution :: Problem p => p s a -> [(a,s)]
私は今日、クラスを完全に取り除き、代わりに次の行に沿って型を使用できることに気づきました
data Problem s a {
initial :: s,
successor :: s -> [(a,s)],
goaltest :: s -> Bool
}
これには拡張機能は必要ありません。関数のシグネチャは見栄えが良くなります。
pathToSolution :: Problem s a -> [(a,s)]
そして、最も重要なことは、型クラスの代わりにこの抽象化を使用するようにコードをリファクタリングした後、以前よりも15〜20%少ない行が残っていることを発見しました。
最大の利点は、型クラスを使用して抽象化を作成したコードでした-以前は、古いデータ構造を複雑な方法でラップする新しいデータ構造を作成し、それらをProblem
クラスのインスタンスに作成する必要がありました(より多くの言語拡張が必要でした)-たくさん比較的単純なことを行うためのコード行の数。リファクタリング後、私はちょうど私が望んでいたことを正確に実行するいくつかの関数を持っていました。
私は今、コードの残りの部分を調べて、型クラスを型に置き換えて、より多くの勝利を収めることができるインスタンスを見つけようとしています。
私の質問は、このリファクタリングはどのような状況で機能しないのでしょうか?実際には、データ型ではなく型クラスを使用する方が良い場合があります。また、これらの状況を事前に認識して、コストのかかるリファクタリングを行う必要がないようにするにはどうすればよいでしょうか。