キーと値のペアの関係の種類に特定の制約を加えた、Data.Map 用の特別なスマート コンストラクターを作成したいと考えています。これは私が表現しようとした制約です:
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, DataKinds #-}
data Field = Speed | Name | ID
data Value = VFloat Float | VString ByteString | VInt Int
class Pair f b | f -> b where
toPair :: f -> b -> (f, b)
toPair = (,)
instance Pair Speed (VFloat f)
instance Pair ID (VInt i)
各フィールドには、関連付ける値のタイプが 1 つだけあります。Speed
私の場合、フィールドを にマップするのは意味がありませんByteString
。Speed
フィールドは一意にマップする必要がありますFloat
しかし、次のタイプのエラーが発生します。
Kind mis-match
The first argument of `Pair' should have kind `*',
but `VInt' has kind `Value'
In the instance declaration for `Pair Speed (VFloat f)'
使用-XKindSignatures
:
class Pair (f :: Field) (b :: Value) | f -> b where
toPair :: f -> b -> (f, b)
toPair = (,)
Kind mis-match
Expected kind `OpenKind', but `f' has kind `Field'
In the type `f -> b -> (f, b)'
In the class declaration for `Pair'
Kind の不一致が発生する理由は理解していますが、toPair
一致Field
しない and で使用するコンパイル時の型チェッカー エラーになるように、この制約をどのように表現できますかValue
。
#haskell から a を使用するように提案されGADT
ましたが、まだ理解できていません。
これの目標は、書くことができるようになることです
type Record = Map Field Value
mkRecord :: [Field] -> [Value] -> Record
mkRecord = (fromList .) . zipWith toPair
Map
キー/値の不変条件が尊重される安全な s を作成できるようにします。
したがって、これは型チェックする必要があります
test1 = mkRecord [Speed, ID] [VFloat 1.0, VInt 2]
しかし、これはコンパイル時エラーであるはずです
test2 = mkRecord [Speed] [VInt 1]
編集:
私の特定の要件は不可能だと思い始めています。私の元の例を使用して
data Foo = FooInt | FooFloat
data Bar = BarInt Int | BarFloat Float
Foo
とに制約を課すには、タイプ レベルで とをBar
区別する何らかの方法が必要です。したがって、代わりに2つのGADTが必要ですFooInt
FooFloat
Bar
data Foo :: * -> * where
FooInt :: Foo Int
FooFloat :: Foo Float
data Bar :: * -> * where
BarInt :: Int -> Bar Int
BarFloat :: Float -> Bar Float
との両方が同じタイプでタグ付けされてPair
いる場合にのみ保持されるのインスタンスを作成できるようになりましたFoo
Bar
instance Pair (Foo a) (Bar a)
そして、私は私が望むプロパティを持っています
test1 = toPair FooInt (BarInt 1) -- type-checks
test2 = toPair FooInt (BarFloat 1) -- no instance for Pair (Foo Int) (Bar Float)
xs = [FooInt, FooFloat]
しかし、それには異種のリストが必要になるため、書く能力を失います。Map
さらに、シノニムを作成しようとすると、タイプのみまたはタイプのみのどちらかでtype FooBar = Map (Foo ?) (Bar ?)
立ち往生しています。これは私が望んでいるものではありません。私が気付いていない強力な型クラスの魔法がない限り、それはかなり絶望的に見えます。Map
Int
Float