2012-05-11 5 views
3

一般的なNumberまたはDivision構造のいずれかである新しい抽象データ型を定義します。ハスケルでどうしたらいいですか?"data MyMath = MyNum Num"のような抽象データ型を定義する方法は?

私の最初のアプローチでした:

data MyMath = MyNum Num 
      | Div MyMath MyMath 

問題は、コンパイラはデータ型が、型クラスではありません「のNum」文句を言うことです。だから私の第二の考えは、このような問題を解決するために、次のようになります。

data MyMath = MyNum Int 
      | MyNum Float 
      | Div MyMath MyMath 

しかしMyNumが2回使用されたとして、これは許可されていないいずれかの動作しません、additionalyこのアプローチは本当に多型ではないでしょう。では、この問題の解決策は何ですか?

EDIT2:答えを読んだ後、私はGADTデータコンストラクタを使用しようとしました。これは、いくつかの人工的なサンプルコードです:

5 data MyMathExpr a where 
6    MyNumExpr :: Num a => a -> MyMathExpr a 
7    MyAddExpr :: MyMathExpr b -> MyMathExpr c -> MyMathExpr (b, c) 
8 deriving instance Show(MyMathExpr a) 
9 deriving instance Eq(MyMathExpr a) 
10 
11 data MyMathVal a where 
12     MyMathVal :: Num a => a -> MyMathVal a 
13 deriving instance Show(MyMathVal a) 
14 deriving instance Eq(MyMathVal a) 
15 
16 foo :: MyMathExpr a -> MyMathVal a 
17 foo (MyNumExpr num) = MyMathVal num 
18 foo (MyAddExpr num1 num2) = MyMathVal (l + r) 
19 where (MyMathVal l) = foo num1 
20   (MyMathVal r) = foo num2 

しかし、何かは、行番号18と間違っている:

test.hs:18:40: 
Couldn't match type `b' with `(b, c)' 
    `b' is a rigid type variable bound by 
     a pattern with constructor 
     MyAddExpr :: forall b c. 
        MyMathExpr b -> MyMathExpr c -> MyMathExpr (b, c), 
     in an equation for `foo' 
     at test.hs:18:6 
In the first argument of `(+)', namely `l' 
In the first argument of `MyMathVal', namely `(l + r)' 
In the expression: MyMathVal (l + r) 

同じことが `C」のために行きます。私はちょうど私が見ていない愚かな間違いだと思います。あなたは?

+3

私はここに任意のブール値は表示されません。 –

+0

ありがとうございました。この質問を投稿しているうちに、私はすべての必要な場所に行っていた変更を忘れることを忘れました。 – Bastian

答えて

3

これは、コード内で対処している問題を解決しますが、ブール値はカバーしません。 あなたがデータ宣言でクラスの制約を使用したい場合、あなたはそれあなたが他の関数となることの方法を実行します。

data (Num a) => MyMath a = MyMath {x :: a}

+4

データコンテキストは使用しないでください。それは間違っているとみなされ、新しいバージョンのGHCで非難されています。 GADTははるかに柔軟です。 – Vitus

0

あなたの試行にはブーリアンは含まれていませんが、代わりにあなたの質問のテキストに行きます。

このタイプを発明する必要はありません。プレリュードのEitherをチェックしてください。あなたが探しているものはです。aNumのインスタンスにします。実際にこれを実施する場合は、以下の編集を準備してください。

編集:Eitherを使用しない場合は、data MyMath a = MyNum a | MyBool Boolを使用できます。今度はとなります。Numのインスタンスであるaが必要ですが、this SO questionthis answer to itを最初に考えるとよいでしょう。実際には、データ型のインスタンスを強制する必要はありません。それをintedadを使って機能させてください。

3

あなたはそのために存在量化を使用することができます。

> let data MyMath = forall n. Num n => MyNum n 
> :t MyNum 3 
MyNum 3 :: MyMath 
> :t MyNum 3.5 
MyNum 3.5 :: MyMath 
+0

存在量の問題は、 "(Num n)= someFunction"のようなパターンバインディングを使うことができないということです。 – Bastian

+0

できます。 'n'はいくつかの_unknown_' Num'型のクラスインスタンスであると仮定するだけです。引き続き問題が発生する場合は、このアプローチに基づいて動作していないコードを表示してください。 –

+0

この特定のエラーを修正することはできますが、間違いなく、存在するタイプは問題には役に立たないことがわかります。 '/'はしばらくの間、 'Num'の一部ではなく、代わりに' + 'を使うという事実を無視しましょう。上のコードから' l'と 'r'を使うつもりです。 'l + r'は意味をなさないでしょうか?あるタイプの 'a'では' l :: Num a => a'が、あるタイプでは 'b'のために' r :: Num b => b'があります。 '+'は引数の型が同じであることを要求しますが、それを知ることはできません。 'a'は' Int'、 'b'は' Float'です! – Vitus

1

それを行う方法はたくさんあります。一つの方法は、GADTsである:

{-# LANGUAGE GADTs #-} 

data MyMath where 
    MyNum :: Num a => a -> MyMath 
    MyBool :: Bool -> MyMath 

GADTsと別の方法:

{-# LANGUAGE GADTs #-} 

data MyMath a where 
    MyNum :: Num a => a -> MyMath a 
    MyBool :: Num a => Bool -> MyMath a 
関連する問題