8

Int を持つすべてのリストの型シノニムを作りたいとしましょう。

私はそれをできた:

type NumberList = [Int]

しかし、数字を含むすべてのリストを NumberList と呼びたい場合はどうすればよいでしょうか? どのように制約を設定し、「Num a」が同じと呼ばれる限り、すべての [a] と言うでしょうか?

編集::答えを見て、再考しました。私は Haskell の背後にある基本的なアイデアの 1 つに反対しているように見えましたが、報酬は比較的少額でした (形式的な問題にすぎません)。私はこれに決めました: Int または Float のみが異なる、互いに同一の 2 つのインスタンスが必要な型の場合、それらの違いは小さすぎて、Int と Float の両方を使用するために必要な回避策を保証するには不十分ですが、それらは同じものです。そのため、使用をそれらの1つに制限する必要があります。ただし、両方を使用する必要がある重要な理由がある場合は、おそらくこの重要な理由をインスタンスの名前に反映して、次のようにして問題を回避できます。

data Thing = Thing_A(String, String Float) | Thing_B(String,String,Int)

--- したがって、Haskell の型付けシステムに固執し、それらの両方をデータ型 Thing として受け入れます。私が最初にやりたかったことは、

data Thing = Thing(String, String, Float) | Thing(String, String, Int)
4

4 に答える 4

10

これは、存在量化に対応します。疑似 Haskell では、

type NumberList = exists a . Num a => [a]

私が「疑似」と言ったのは、GHC がその場で存在量指定子を導入することを許可していないためです。そのための別のデータ型を作成する必要があります。

さて、NumberList を使用するタイプのほとんどは、矢印の左側で、«exists» が効果的にその意味を «forall» に変更します。

つまり、書く代わりに

isIncreasing :: NumberList -> Bool

これはと同じです

isIncreasing :: (exists a . Num a => [a]) -> Bool

あなたは書ける

isIncreasing :: forall a . Num a => [a] -> Bool

または単に

isIncreasing :: Num a => [a] -> Bool

もちろん、型シノニムを使用するとコードが少なくなりますが、欠点もあります。ところで、これらの欠点は、実存的アプローチに基づくオブジェクト指向プログラミングに典型的なものです。

たとえば、2 つのリストを連結するとします。通常、あなたは書くでしょう

(++) :: forall a . [a] -> [a] -> [a]

(ここでもforall不要であり、明確にするために追加されています)。a署名全体で同じであるため、同じタイプのリストを連結していることを確認できます。

2 つの数値リストを連結するにはどうすればよいでしょうか? サイン

(++) :: NumberList -> NumberList -> NumberList

1 つのリストには Int が含まれ、もう 1 つのリストには Double が含まれる可能性があるため、機能しません。結果の NumberList には、単一の型の値が含まれている必要があります。

または、たとえば、リスト要素の合計を見つけたいとします。

通常、あなたは書く

sum :: Num a => [a] -> a

結果の型がリスト要素の型と同じであることに注意してください。残念ながら、NumberList に対して同じことを行うことはできません。

sum :: NumberList -> ???

結果のタイプは何ですか?そこにも存在量化を適用できます。

sum :: NumberList -> (exists a . Num a => a)

しかし、元のリスト型と sum 型の間の接続は失われています — 少なくとも Haskell の型システムでは。次に、次のような関数を作成することにした場合

multiplySum :: Integer -> [Integer] -> Integer
multiplySum x ys = x * sum ys

sum ys整数型であるとは限らず、任意の型である可能性があるため、型エラーが発生します。

すべてを極限まで推し進めて、すべての型を存在論的に定量化すればうまくいくでしょう。

(とは言っても、もちろん、存在量化にはいくつかの良い使用例があります。)

于 2012-10-03T23:25:11.600 に答える
6

コンテキストを取得するために存在要素ではなく、データとパラメーターを使用する

あなたが望むなら私は思う

data Thing = Good [(Char,Int)] | Bad String | Indifferent Leg

でも時には

data Thing = Good [(Char,Float)] | Bad String | Indifferent Arm

あなたが定義することができます

data Thing num bodypart = Good [(Char,num)] | Bad String | Indifferent bodypart

または、常に数値であることを確認したい場合numは、次のことができます

data Num num => Thing num bodypart = Good [(Char,num)] | Bad String | Indifferent bodypart

bodypart最後に、独自のクラスを定義することで、許可される型を制限できます。

class Body a where
   -- some useful function(s) here

instance Body Leg where
   -- define useful function(s) on Legs
instance Body Arm
   -- define useful function(s) on Arms

data (Num num,Body bodypart) => Thing num bodypart = 
                                                             Good [(Char,num)] | Bad String | Indifferent bodypart

forall コンストラクターまたは GADT を介して存在型を使用することを思いとどまらせたいと思いnumます。データ型にパラメーターを追加すると、より多くの入力が必要になりますが、実際にはかなり役立つからです。

型シノニムの制約?

次のような制約を使用する場合は、

data (Num num) => Thing num = T [(Char,num)]

実際にはコンストラクターの型を変更するだけTです

T :: (Num num) => [(Char,num)] -> Thing num

の代わりにT :: [(Char,num)] -> Thing num。これは、 を使用するたびTに context が必要であることを意味します(Num num)が、それは実際にあなたが望んでいたことです - 人々があなたのデータ型に数値ではないデータを入れないようにするためです。

この事実の結果は、あなたが書くことができないということです

type Num num => [(Char,num)]

Tコンテキスト(Num num)を必要とするデータ コンストラクター関数がないためです。[('4',False)] がある場合、それはシノニム[(Char,num)]であるため、自動的にタイプに一致します。コンパイラは、何かの型を決定する前に、インスタンスを探してコードを実行することはできません。この場合、型を伝えるコンストラクターがあり、 function の使用をチェックしたため、インスタンスがあることを保証できます。いいえ、コンテキストはありません。dataTNum numTT

于 2012-10-04T06:13:38.307 に答える
2

GHCは、RankNTypesを使用してこれを許可します。

だからあなたはこれを行うことができます:

type NumList = forall a . (Num a ,Fractional a) => [a]

そして、もし私たちが持っているなら:

numList:: NumList
numList = [1,2,3]

fracList:: NumList
fracList = [1.3,1.7]

連結を行うと、次のようになります。

fracList ++ numList :: Fractional a => [a]

NumListはの同義語です。全体として、この場合、私は本当に要点を理解していません。

于 2012-10-03T23:52:22.283 に答える
0

元の型を復元できない場合、そのような型を使用してもあまり意味がありません。特定Numの のみが必要な場合は、強力な魔法を呼び出す代わりに、単純にそれらをラップする必要があります。

data NumWrapper = WInt Int 
                | WDouble Double 
                | WFloat Float 
                deriving Show

numList :: [NumWrapper]
numList = [WInt 12, WFloat 1.2, WDouble 3.14]

任意の型に対して本当にオープンになりたい場合Numは、リストはあなたにとって間違ったコレクションかもしれません。HList などがあります。http: //www.haskell.org/haskellwiki/Heterogenous_collections を参照してください。

于 2012-10-04T06:39:01.420 に答える