6

列挙可能で境界のある型のすべての値を列挙するユーティリティ関数があります。

enumerate :: (Enum a, Bounded a) => [a]
enumerate = [minBound .. maxBound]

および列挙可能な型を整数にマッピングするデータ型:

data Attribute a = Attribute { test :: a -> Int
                             , vals :: [Int]
                             , name :: String }

vals可能なすべての列挙可能な値を表す整数のリストはどこにありますか。たとえば、私が持っていた場合

data Foo = Zero | One | Two deriving (Enum,Bounded)

それならvalsそうでしょう[0,1,2]

aを列挙可能な型にマップする関数と名前を指定するだけで、これらの属性をプログラムで作成できるようにしたいと考えています。このようなもの:

attribute :: (Enum b, Bounded b) => (a -> b) -> String -> Attribute a
attribute f str = Attribute (fromEnum . f) vs str
  where
    vs = map fromEnum enumerate

enumerate呼び出しをb型シグネチャでに接続する方法がないため、これは型チェックしません。だから私はこれができると思った:

vs = map fromEnum $ enumerate :: [b]

しかし、それもコンパイルされません-コンパイラはそれをに名前を変更しbますb1. GADTs 拡張機能を使用して、より賢くしようとしました。

attribute :: (Enum b, Bounded b, b ~ c) => {- ... -}
vs = map fromEnum $ enumerate :: (Enum c,Bounded c) => [c]

ただし、cは に名前が変更されていc1ます。

bの型をパラメーターとして型に含めたくありませんAttribute(主に、の値が異なる可能性のある属性のリストを格納したいためです。それが、 has typeとhas typebの理由です)。testa -> Intvals[Int]

やりたいことを実行できるように、このコードをどのように書くことができますか?

4

1 に答える 1

6

型変数の問題は、それらが型シグネチャでのみバインドされることです。定義内で型変数を使用すると、新しい新しい型変数が参照されます (型シグネチャとまったく同じ名前であっても)。

シグネチャから型変数を参照するには、ScopedTypeVariables拡張子とasTypeOf.

ScopedTypeVariables明示的にバインドされた型変数もforall定義で使用できるため、次のようになります。

attribute :: forall a b. (Enum b, Bounded b) =>
             (a -> b) -> String -> Attribute a
attribute f str = Attribute (fromEnum . f) vs str
  where
    vs = map fromEnum (enumerate :: [b])

もう 1 つの方法には、次のようにasTypeOf定義された関数が含まれます。

asTypeOf :: a -> a -> a
asTypeOf = const

type の式[b]を 2 番目のパラメーターに取得できる場合、統合により、最初のパラメーターにも type があることが確認されます[b]f :: a -> bとがあるのでf undefined :: b、次のように書くことができます。

attribute :: (Enum b, Bounded b) => (a -> b) -> String -> Attribute a
attribute f str = Attribute (fromEnum . f) vs str
  where
    vs = map fromEnum (enumerate `asTypeOf` [f undefined])
于 2012-07-29T16:01:07.437 に答える