1

プロジェクトには、異なるモジュールで定義されたいくつかの異なるタイプがあり、それぞれに関連する関数があります (関数は同じ名前で非常に似た意味を持っているため、以下は理にかなっています)。ここで、これらすべてのタイプのインスタンスを (同時に) 持つことができるリストを作成したいと思います。私が考えることができる唯一の可能性は、次のようなものです:

data Common = A{...} | B{...} | ...

ただし、定義を異なるモジュール (A、B、...) ではなく、1 つの場所に保持することを意味します。これを行うより良い方法はありますか?

UPD

私は haskell にかなり慣れていないので、勉強に関連するプログラムをいくつか書いています。この場合、さまざまFormalLanguageな定義方法があります:FiniteAutomataなどGrammars。それぞれに共通の関数 ( isAcceptedrepresentation、...) があるため、要素がこれらの型のいずれかであるリストを作成することは理にかなっているように思われました。

4

2 に答える 2

6

正しい解決策は個別の型をリストに格納することであると想定することで、OOP の考え方を Haskell にもたらしています。その仮定を調べることから始めます。

通常、異なる型は同種のリストに格納します。これらは共通のインターフェイスをサポートするためです。共通のインターフェイスを除外して、それをリストに保存しないのはなぜですか?

残念ながら、あなたの質問はその共通インターフェースが何であるかを説明していないので、デモンストレーションとしていくつかの一般的な例を紹介します.

最初の例は、署名を持つ関数をすべてサポートする一連の値 、xyおよびです。zShow

(Show a) => a -> String

後で表示したい型を保存する代わりにshow、値を直接呼び出して、結果の文字列をリストに保存することができます。

list = [show x, show y, show z] :: String

showHaskell は遅延言語であり、show実際に文字列が必要になるまで実際には s を評価しないため、時期尚早に呼び出してもペナルティはありません。

または、型が次のような複数のメソッドをサポートしている可能性があります。

class Contrived m where
    f1 :: m -> String -> Int
    f2 :: m -> Double

上記の形式のクラスを、メソッドを値に部分的に適用した結果を含む同等の辞書に変換できます。

data ContrivedDict = ContrivedDict {
    f1' :: String -> Int,
    f2' :: Double }

...そして、このディクショナリを使用して、サポートすることが期待される共通インターフェイスに任意の値をパッケージ化できます。

buildDict :: (Contrived m) => m -> ContrivedDict
buildDict m = ContrivedDict { f1' = f1 m, f2' = f2 m }

次に、この共通インターフェース自体をリストに保存できます。

list :: [buildDict x, buildDict y, buildDict z]

ここでも、明確に型指定された値を格納する代わりに、それらの共通要素を取り出してリストに格納しました。

ただし、このトリックは常に機能するとは限りません。異常な例は、次の型を持つクラスの(+)演算子など、同じ型の 2 つのオペランドを期待する任意の二項演算子です。Num

(Num a) => a -> a -> a

私の知る限り、二項演算を部分的に適用し、それが同じ型の 2 番目のオペランドに適用されることを保証するような方法で格納するための、辞書ベースの優れたソリューションはありません。このシナリオでは、存在型クラスがおそらく唯一の有効なアプローチです。ただし、型クラス ベースのアプローチよりも強力なトリックと変換が可能になるため、可能な限り辞書ベースのアプローチを使用することをお勧めします。

この手法の詳細については、Luke Palmer の記事Haskell Antipattern: Existential Typeclassを読むことをお勧めします。

于 2012-09-22T14:49:22.713 に答える
5

いくつかの可能性があります:

可能性 1:

data Common = A AT | B BT | C CT

AT、BT、CT をそれぞれのモジュールに記述

可能性 2:

{-# LANGUAGE ExistentialQuantification #-}

class CommonClass a where
    f1 :: a -> Int

data Common = forall a . CommonClass a => Common a

これは OOP スーパークラスとほぼ同じですが、「ダウンキャスト」はできません。その後、すべてのモジュールで共通クラスのメンバーの実装を宣言できます。

@Gabriel Gonzalez によって提案された可能性 3:

data Common = Common {
     f1 :: Int
}

したがって、モジュールは、クロージャーを使用して「プライベート」部分を抽象化することにより、共通のインターフェイスを実装します。

ただし、Haskell の設計は通常、OOP の設計とは根本的に異なります。Haskell ですべての OOP トリックを実装することは可能ですが、慣用的ではない可能性が高いため、@dflemstr が言ったように、問題に関する詳細情報を歓迎します。

于 2012-09-22T10:59:01.460 に答える