Desidero creare un costruttore intelligente speciale per Data.Map con un certo vincolo sui tipi di relazioni coppia chiave/valore. Questo è il vincolo ho cercato di esprimere:Garanzia statica sulle relazioni chiave/valore in Data.Mappa
{-# 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)
per ogni campo, esiste un solo tipo di valore che deve essere associato. Nel mio caso, non ha senso che un campo Speed
si associ a un ByteString
. Un campo Speed
dovrebbe mappare unicamente ad un Float
Ma ottengo il seguente errore di tipo:
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)'
utilizzando -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'
capisco perché ho ottenere il tipo mis-match, ma come posso esprimere questo vincolo in modo che sia un errore di tipo "check-up" di compilazione per utilizzare toPair
su un valore non corrispondente Field
e Value
.
Mi è stato suggerito da #haskell di utilizzare uno GADT
, ma non sono ancora riuscito a capirlo.
L'obiettivo di questo è quello di essere in grado di scrivere
type Record = Map Field Value
mkRecord :: [Field] -> [Value] -> Record
mkRecord = (fromList .) . zipWith toPair
in modo che io possa fare sicuri Map
s ove siano rispettate le invarianti chiave/valore.
Quindi questo dovrebbe digitare-check
test1 = mkRecord [Speed, ID] [VFloat 1.0, VInt 2]
ma questo dovrebbe essere un errore di tempo di compilazione
test2 = mkRecord [Speed] [VInt 1]
EDIT:
Sto cominciando a pensare che le mie esigenze specifiche aren' t possibile. Usando il mio esempio originale
data Foo = FooInt | FooFloat
data Bar = BarInt Int | BarFloat Float
Al fine di far rispettare il vincolo sulla Foo
e Bar
, ci deve essere un modo per distinguere tra un FooInt
e FooFloat
a livello di tipo e allo stesso modo per Bar
. Così ho invece bisogno di due GADTs
data Foo :: * -> * where
FooInt :: Foo Int
FooFloat :: Foo Float
data Bar :: * -> * where
BarInt :: Int -> Bar Int
BarFloat :: Float -> Bar Float
ora posso scrivere un'istanza per Pair
che tiene solo quando il Foo
e Bar
sono entrambi etichettati con lo stesso tipo
instance Pair (Foo a) (Bar a)
e ho le proprietà che voglio
test1 = toPair FooInt (BarInt 1) -- type-checks
test2 = toPair FooInt (BarFloat 1) -- no instance for Pair (Foo Int) (Bar Float)
ma perdono la capacità di scrivere xs = [FooInt, FooFloat]
, perché ciò richiederebbe una lista eterogenea.Inoltre se provo a rendere il sinonimo Map
type FooBar = Map (Foo ?) (Bar ?)
sono bloccato con uno Map
di entrambi i tipi Int
o solo i tipi Float
, che non è quello che voglio. Sembra piuttosto senza speranza, a meno che non ci sia qualche potente stregoneria di tipo classe di cui non sono a conoscenza.
Hai provato [firme esplicito tipo] (http://www.haskell.org/ghc/docs/7.4.2/html/users_guide/other-type-extensions.html#kinding)? Solo curioso. –
Perché non utilizzare una famiglia di dati. Quindi il tipo di mappa può essere legato al tipo di chiave. – MFlamer
Queste mie domande sono simili e potrebbero essere d'aiuto. http://stackoverflow.com/questions/14949021/return-type-as-a-result-of-term-or-value-calculation http://stackoverflow.com/questions/14918867/trouble-with-datakinds – MFlamer