16

同等の値を保持するコンストラクターと保持しないコンストラクターを持つ代数的データ型があります。(==)標準や(/=)演算子のように機能するいくつかの比較関数をNothing作成しましたが、意味をなさない比較に戻ります。

data Variant = IntValue Int
             | FloatValue Float
             | NoValue

equal :: Variant -> Variant -> Maybe Bool
equal (IntValue a) (IntValue b) = Just (a == b)
equal (FloatValue a) (FloatValue b) = Just (a == b)
equal _ _ = Nothing

unequal :: Variant -> Variant -> Maybe Bool
unequal (IntValue a) (IntValue b) = Just (a /= b)
unequal (FloatValue a) (FloatValue b) = Just (a /= b)
unequal _ _ = Nothing

それは機能しますが、繰り返しは扱いにくいです。特に、実際にはVariantコンストラクターと比較関数が多いためです。

比較関数でパラメーター化されたヘルパー関数に繰り返しを因数分解できると思いました。

helper :: (Eq a) => (a -> a -> Bool) -> Variant -> Variant -> Maybe Bool
helper f (IntValue a) (IntValue b) = Just (f a b)
helper f (FloatValue a) (FloatValue b) = Just (f a b)
helper _ _ _ = Nothing

equal' :: Variant -> Variant -> Maybe Bool
equal' = helper (==)

unequal' :: Variant -> Variant -> Maybe Bool
unequal' = helper (/=)

しかし、型変数は明らかに両方にaバインドできず、同時に;の定義にバインドできないため、これは機能しません。GHCはそれをにバインドし、を処理する行の型の不一致について文句を言います。IntFloathelperFloatIntValue

のような関数(==)は、直接使用すると多形になります。それを別の関数に渡して多形のままにする方法はありますか?

4

1 に答える 1

15

はい、これは可能ですが、言語拡張でのみ可能です:

{-# LANGUAGE Rank2Types #-}

helper :: (forall a. (Eq a) => (a -> a -> Bool))
       -> Variant -> Variant -> Maybe Bool
helper f (IntValue a) (IntValue b) = Just (f a b)
helper f (FloatValue a) (FloatValue b) = Just (f a b)
helper _ _ _ = Nothing

forall a.、それがどのように聞こえるかについて行います。これaは、括弧内では普遍的に量化され、括弧の外では範囲外です。これは、f引数が のインスタンスであるすべての型 a に対してポリモーフィックである必要があることを意味しEqます。これはまさにあなたが望むものです。

ここでの拡張は「ランク 2」と呼ばれます。これは、最も外側のスコープで通常のスタイルのポリモーフィズムに加えて、ここの例のようにポリモーフィックな引数を許可するためです。物事をさらにネストするには、拡張機能が必要ですRankNTypes。これはかなり自己記述的です。

余談ですが、上位のポリモーフィック型に関しては、forallが実際に変数を型にバインドするものであることに注意してください。実際、それらはラムダのように振る舞うと考えることができます。このような関数を具体的な型を持つものに適用すると、引数の型はその使用のforallために暗黙的にバインドされます。これは、たとえば、forallその関数の内側の外側によって型がバインドされた値を使用しようとした場合に発生します。値の型が範囲外になっているため、賢明なことを行うことが難しくなっています (ご想像のとおり)。

于 2011-08-15T04:02:50.927 に答える