2011-09-17 16 views
1

mykelコードをモジュール化してもらえません。私はおそらくオブジェクト指向のパラダイムに詰まっていて、機能的に考えるのに問題がありますが、私は完全に困惑しています。関数定義を複数のファイルに分割することでモジュール性が向上する

conjunction :: TruthType -> TruthType -> TruthType 
conjunction (TT_Percent x) (TT_Percent y) = TT_Percent (x*y) 
conjunction (TT_Boolean "t") (TT_Boolean "t") = TT_Boolean "t" 
conjunction (TT_Boolean "t") (TT_Boolean "f") = TT_Boolean "f" 
conjunction (TT_Boolean "f") (TT_Boolean "t") = TT_Boolean "f" 
conjunction (TT_Boolean "f") (TT_Boolean "f") = TT_Boolean "f" 

disjunction :: TruthType -> TruthType -> TruthType 
disjunction (TT_Percent x) (TT_Percent y) = TT_Percent (x + (1-x)*y) 
disjunction (TT_Boolean "t") (TT_Boolean "t") = TT_Boolean "t" 
disjunction (TT_Boolean "t") (TT_Boolean "f") = TT_Boolean "t" 
disjunction (TT_Boolean "f") (TT_Boolean "t") = TT_Boolean "t" 
disjunction (TT_Boolean "f") (TT_Boolean "f") = TT_Boolean "f" 

これは、コンパイルして実行:

data TruthType = TT_Boolean String 
       | TT_Percent Double 

conjunction :: TruthType -> TruthType -> TruthType 
disjunction :: TruthType -> TruthType -> TruthType 

通常は、あなたがこのように、右隣同士に、これらの機能を実装します:

私は、データとそれを操作する二つの機能を持っています私がそれを期待するのとまったく同じです。問題は、約20種類のTruthTypesを実装することを計画していることです。これらのセクションの両方が同じファイル内にある場合

-- TT_Percent 
conjunction (TT_Percent x) (TT_Percent y) = TT_Percent (x*y) 
disjunction (TT_Percent x) (TT_Percent y) = TT_Percent (x + (1-x)*y) 

-- TT_Boolean 
conjunction (TT_Boolean "t") (TT_Boolean "t") = TT_Boolean "t" 
conjunction (TT_Boolean "t") (TT_Boolean "f") = TT_Boolean "f" 
conjunction (TT_Boolean "f") (TT_Boolean "t") = TT_Boolean "f" 
conjunction (TT_Boolean "f") (TT_Boolean "f") = TT_Boolean "f" 

disjunction (TT_Boolean "t") (TT_Boolean "t") = TT_Boolean "t" 
disjunction (TT_Boolean "t") (TT_Boolean "f") = TT_Boolean "t" 
disjunction (TT_Boolean "f") (TT_Boolean "t") = TT_Boolean "t" 
disjunction (TT_Boolean "f") (TT_Boolean "f") = TT_Boolean "f" 

が、私はコンパイルエラーが、私は接続詞を再定義していますことを主張し得る:だから、それは彼らがに作用しているTruthTypeコンストラクタに基づいて、グループに私の機能をより理にかなっています分離関数。私は古い定義を消去したくないので、両方の定義を有効にします。この再定義を許可するために使用できるコンパイラフラグはありますか?

最終的に私の目標は、これらの異なるTruthTypeをそれぞれ独自のファイルに定義することです。私がそれをすると、使用する関数が分からないのであいまいなエラーが出ます。実際に呼び出されているTruthTypeで定義されるのは1つだけなので、GHCにそれらのすべてを試す方法はありますか?

PS。これは型クラスの大きなユースケースのように思えるかもしれませんが、実際はそうではありません。

class (Show a, Eq a) => TruthClass a where 
    conjunction :: a -> a -> a 
    disjunction :: a -> a -> a 

instance TruthClass Bool where 

    conjunction True True = True 
    conjunction True False = False 
    conjunction False True = False 
    conjunction False False = False 

    disjunction True True = True 
    disjunction True False = True 
    disjunction False True = True 
    disjunction False False = False 

instance TruthClass Double where 
    conjunction x y = x*y 
    disjunction x y = x + (1-x)*y 

classReturn :: (TruthClass a) => String -> a -- This fails to compile because it would allow the failure function below, which violates conjunction's type 
classReturn "True" = True 
classReturn "False" = False 
classReturn "1" = 1 
classReturn "0" = 0 

failure = conjunction (classReturn "True") (classReturn "1") 

編集:

さて、私は今よりよく説明することができ、私は、「インスタンス」TruthTypeの、この例では「classReturn」機能のようなものを返す関数を書くことができるようにする必要がありますどうして私は型クラスを動作させることができなかったのですか?また、提供されたソリューションがなぜ私にとってうまくいかないのですか? (以下augustssのソリューションに基づいて)次を見てください:

*Main> conjunction True True -- works because type is inferred 
True 

*Main> classReturn "True" :: Bool -- works because type is explicitly stated 
True 

*Main> classReturn "True" -- does not work, but this is what I need 

<interactive>:1:0: 
    Ambiguous type variable `a' in the constraint: 
     `TruthClass a' 
     arising from a use of `classReturn' at <interactive>:1:0-17 
    Probable fix: add a type signature that fixes these type variable(s) 

私のプログラムでは、私はそれがどのタイプを指定することはできません。私はparsecを使って入力ファイルを解析しています。行 "#bool"にヒットすると、その後に生成されるすべての変数はTT_Boolean型でなければなりません。それが "#percent"になると、後続の変数はすべてTT_Percent型でなければなりません。したがって、私は関数を呼び出すときに型がどのようになるかを厳密に記述することはできません。型クラスを使用する場合は、ハードコードする必要があるようです。データを使用するソリューションはこの問題を解決しますが、データに起因するモジュール性の欠如に陥ります。

+0

'classReturn'もオーバーロードしないのはなぜですか?ちょうどそれをクラスのメンバーにしてください。 – augustss

+0

ところで、私は 'x + y - x * y'は対称性を強調していると思いますが、バージョンに数値的な理由があるかもしれません。 – augustss

+0

あなたのコメントで何かが失われました。 – augustss

答えて

3
class (Read a, Show a, Eq a) => TruthClass a where 
    conjunction :: a -> a -> a 
    disjunction :: a -> a -> a 
    classReturn :: String -> a 
    classReturn = read 

instance TruthClass Bool where 
    conjunction = (&&) 
    disjunction = (||) 

instance TruthClass Double where 
    conjunction x y = x*y 
    disjunction x y = x + (1-x)*y 
+0

何らかの理由で、私はクラスにある関数がそのクラスをパラメータとして使用しなければならないと考えていました。私はいくつかの周りを再生する必要がありますが、私はこれが動作すると思います。ありがとう。 –

+2

クラスパラメータを返すことは、OOクラスを上回るHaskell型クラスの利点の1つです。 – augustss

+2

@Mike Izbicki:クラス内のものは、実際にはパラメータを必要としません。クラスの一部として特定の値を持つこともできます - [Monoid'を見てください](http://hackage.haskell.org/packages/archive/base/4.4.0.0/doc/html/Data-Monoid。 html)などがあります。クラス自体は、より抽象的なものであることを覚えておいてください。型パラメータは、インスタンスを定義するために使用される具体的な型を表します。あなたがすることができない唯一のことは、それを全く言及していないことです。 –

0

しかし、あなたはまた、あなたがconjunctionのための方程式とその逆の間disjunctionのための方程式を持っていなければならないだけで、あなたのオリジナルデザインを維持することができます。

関数はすべての式から成り立ちますが、ソースコード内で連続している必要があります。

EDIT:例を示してどのようにマイクを行うことができます望んでいる:

あなたは多くの句は、あなたが複数のものにあなたの1つの偉大な機能を分割することができますことをお持ちの場合:

conjunction PrincipleCase1 = conjunctionForCase1 ... 
conjunction PrincipleCase2 = conjunctionForCase2 ... 

してからあなたを詳細なケースをさまざまな位置、モジュールなどに組み込むことができます。

+0

通常、よりクリーンなコードを作成する点を除いて、連続して実行する必要がある理由は考えられません。 (もちろん、私の場合は例外です。)非連続的な定義を許すように強制する方法があるようです。 –

+0

マイク、私の編集を参照してください。 – Ingo

関連する問題