5

だから、私は文脈上の意味で楽しい実験に取り組んでいて、壁にぶつかっています。あるコンストラクターから別のコンストラクターに変換されるプリミティブまたは関数のいずれかになるデータ型を定義しようとしています。

data WeaponPart =
    WInt Int |
    WHash (Map.Map String Int) |
    WNull |
    WTrans (WeaponPart -> WeaponPart)

instance Show WeaponPart where
    show (WInt x) = "WInt " ++ (show x)
    show (WHash x) = "WHash " ++ (show x)
    show (WTrans _) = "WTrans"
    show WNull = "WNull"

cold :: WeaponPart -> WeaponPart
cold (WInt x) = WHash (Map.singleton "frost" x)
cold (WHash x) = WHash $ Map.insertWith (+) "frost" 5 x
cold (WTrans x) = cold $ x (WInt 5)
cold (WNull) = cold $ (WInt 5)

ofTheAbyss :: WeaponPart -> WeaponPart
ofTheAbyss (WTrans x) = x (WTrans x)

問題は、署名がofTheAbyss引数として WeaponPart を許可しているのに対し、私は WTrans で構築された引数のみを許可したいということです。その場合のパターン マッチしか書いていないことがわかります。

GADT を試してみましたが、うさぎの穴だったのではないかと思います。私がやりたいことを彼らにやらせることは決してできませんでした。WTrans引数のみをofTheAbyssに強制する方法を知っている人はいますか? それとも、何かが完全に欠けているだけですか。

ありがとう。

ベスト、エリック

4

3 に答える 3

10

GADT を使用すると、この種のことができます。結果がうさぎの穴かどうかを判断するのは私ではありませんが、少なくともレシピをお見せしましょう. 私は新しいPolyKinds拡張機能を使用していますが、それより少ない量で管理できます。

まず、必要なものを決定し、それらの種類のデータ型を定義します。

data Sort = Base | Compound

次に、並べ替えによってインデックス付けされたデータを定義します。これは、小さな型付き言語を構築するようなものです。

data WeaponPart :: Sort -> * where
  WInt    :: Int ->                                   WeaponPart Base
  WHash   :: Map.Map String Int ->                    WeaponPart Base
  WNull   ::                                          WeaponPart Base
  WTrans  :: (Some WeaponPart -> Some WeaponPart) ->  WeaponPart Compound

次のように、存在量化を介して「あらゆる種類のデータ」を表すことができます。

data Some p where
  Wit :: p x -> Some p

xはエスケープしないことに注意してください。ただし、 x「満たす」という「証拠」を検査することはできpます。は存在するsに対する GHC オブジェクトとしてではなく、型でSomeなければならないことに注意してください。datanewtypenewtype

Sortこれで、ジェネリック操作を自由に記述できるようになりました。一般的な入力がある場合は、ポリモーフィズムを使用して、 として効果的にカリー化できSome p -> ...ますforall x. p x -> ...

instance Show (WeaponPart x) where
  show (WInt x)    = "WInt " ++ (show x)
  show (WHash x)   = "WHash " ++ (show x)
  show (WTrans _)  = "WTrans"
  show WNull       = "WNull"

ジェネリック出力には実存が必要ですSort。ここでは、入力と出力に使用します。

cold :: Some WeaponPart -> Some WeaponPart
cold (Wit (WInt x))    = Wit (WHash (Map.singleton "frost" x))
cold (Wit (WHash x))   = Wit (WHash $ Map.insertWith (+) "frost" 5 x)
cold (Wit (WTrans x))  = cold $ x (Wit (WInt 5))
cold (Wit WNull)       = cold $ Wit (WInt 5)

ちょくちょくWit場所をいじってますが、同じ番組です。

その間、私たちは今書くことができます

ofTheAbyss :: WeaponPart Compound -> Some WeaponPart
ofTheAbyss (WTrans x) = x (Wit (WTrans x))

したがって、組み込み型システムを扱うのは恐ろしいことではありません。場合によってはコストがかかります: 組み込み言語にサブソートを持たせたい場合、一部のデータ型のインデックスを変更するためだけに余分な計算を行い、データ自体に違いがないことに気付くかもしれません。サブソートが必要ない場合は、追加の規律が真の友になることがよくあります。

于 2012-05-27T10:07:16.577 に答える
3

別の解決策として、データ型を 2 つに分割します。他の回答と一致する名前を使用して、類似点を簡単に確認できるようにしました。

data WeaponPartBase
    = WInt Int
    | WHash (Map.Map String Int)
    | WNull

data WeaponPartCompound = WTrans (WeaponPart -> WeaponPart)
data WeaponPart = Base WeaponPartBase | Compound WeaponPartCompound

cold :: WeaponPart -> WeaponPart
cold (Base (WInt x)) = Base (WHash (Map.singleton "frost" x))
cold (Base (WHash x)) = Base (WHash $ Map.insertWith (+) "frost" 5 x)
cold (Base WNull) = cold (Base (WInt 5))
cold (Compound (WTrans x)) = cold (x (Base (WInt 5))

ofTheAbyss :: WeaponPartCompound -> WeaponPart
ofTheAbyss (WTrans x) = x (WCompound (WTrans x))

これは、基本的なものに対してクラスを宣言することで、もう少し便利にすることができます。

class Basic a where
    wint :: Int -> a
    whash :: Map.Map String Int -> a
    wnull :: a

class Compounded a where
    wtrans :: (WeaponPart -> WeaponPart) -> a

instance Basic WeaponPartBase where
    wint = WInt
    whash = WHash
    wnull = WNull

instance Basic WeaponPart where
    wint = Base . wint
    whash = Base . whash
    wnull = Base wnull

instance Compounded WeaponPartCompound where
    wtrans = WTrans

instance Compounded WeaponPartCompound where
    wtrans = Compound . wtrans

たとえばcoldofTheAbyss代わりに次のようになります。

cold' (Base (WInt x)) = whash (Map.singleton "frost" x)
cold' (Base (WHash x)) = whash $ Map.insertWith (+) "frost" 5 x
cold' (Base WNull) = cold' (wint 5)
cold' (Compound (WTrans x)) = cold' (x (wint 5))

ofTheAbyss' (WTrans x) = x (wtrans x)
于 2012-05-27T15:09:58.770 に答える
1

関数を型ではなくコンストラクターで制約しようとしています。それは実行可能なことではありません。

実際、それは実行可能なことではありません-別の関数を作成していて、不明なものがある場合WeaponPart、それをに渡すことができるかどうかが必要です-それは型チェックする必要がありますofTheAbyss

私が考えることができる2つのオプションは次のとおりです。

a) ofTheAbysstype(WeaponPart -> WeaponPart) -> WeaponPartを指定し、コンストラクターを「アンパック」します。

b)ofTheAbyss他のコンストラクターで実行時エラーを発生させた。

 ofTheAbyss :: WeaponPart -> WeaponPart
 ofTheAbyss (WTrans x) = x (WTrans x)
 ofTheAbyss _ = error "Illegal argument to ofTheAbyss was not a WTrans"
于 2012-05-27T07:15:47.067 に答える