12
votes

Garantie statique sur les relations clés / valeurs dans les données.map

Je veux faire un constructeur intelligent intelligent pour les données.MAP avec une certaine contrainte sur les types de relations de paires de clé / de valeur. C'est la contrainte que j'ai essayée d'exprimer: xxx

pour chaque champ, il n'existe qu'un seul type de valeur qu'il doit être associé. Dans mon cas, il n'a aucun sens pour un champ pour mapper sur un bytestring . Un champ vitesse doit planer uniquement sur un float

mais je reçois l'erreur de type suivante: xxx

à l'aide de -xkindsignatures : xxx

Je comprends pourquoi je reçois le type de malaque, mais comment puis-je exprimer cette contrainte pour que ce soit est une erreur de type temporisation-checker pour utiliser topair sur un champ non correspondant et de valeur .

C'était m'a suggéré de par #hakell pour utiliser un gadt , mais je n'ai pas pu le comprendre encore.

L'objectif est de pouvoir écrire xxx

de sorte que je puisse faire de sécurité cartographique s où les invariants de clé / valeur sont respectés.

donc cela devrait donc vérifier xxx

mais cela doit être une erreur de temps de compilation xxx

édition:

Je commence à Pensez que mes besoins spécifiques ne sont pas possibles. Utilisation de mon exemple original xxx

afin d'appliquer la contrainte sur FOO et bar , il doit y avoir un moyen de Différencier entre un FOOINT et FOOfloat au niveau de type et de la même manière pour bar . Ainsi, j'ai besoin plutôt de deux gadits xxx

maintenant, je peux écrire une instance pour la paire qui ne contient que lorsque le foo et bar sont tous deux étiquetés avec le même type xxx

et j'ai les propriétés que je veux xxx p> Mais je perds la possibilité d'écrire xs = [fooint, foofloat] car cela nécessiterait une liste hétérogène. En outre, si j'essaie de faire la carte Synonyme Tapez foobar = carte (foo?) (Bar?) Je suis coincé avec une carte de Soit seulement int types ou uniquement float Types, ce qui n'est pas ce que je veux. Il cherche plutôt désespérément, à moins que la magie de classe de type puissante ne me connaisse pas.


10 commentaires

Avez-vous essayé Signatures type explicite ? Juste curieux.


Pourquoi ne pas utiliser une famille de données. Ensuite, le type de carte peut être attaché au type de clé.


Ces questions de mienne sont similaires et pourraient aider. Stackoverflow.com/questions/14949021/... Stackoverflow.com/ Questions / 14918867 / Trouble-avec-Datakinds


Une question de simpleston: si la correspondance est une à une, pourquoi ne pas utiliser une seule définition de données simple champ de données = flotteur de vitesse | Nom bytestring | Id int ? Vous pouvez simplement écrire test1 = [vitesse 1.0, ID 2] , mais test2 = [vitesse (1 :: int)] donnerait une erreur de compilation. Que voulez-vous faire ce serait impossible de cette façon? Avez-vous vraiment besoin d'avoir des champs non conseillants?


@Willness: Je dois être capable de rechercher la valeur liée au champ , et il doit être similaire à carte parce que j'ai besoin Diffusion non instantané s. Si le champ contient toutes les informations, il défait le point de recherches car je vais devoir connaître la valeur que le champ contient.


Si vous n'avez que trois types de champ, pourquoi ne pas avoir trois clés de chaîne, ou trois INTS même (1,2 et 3), c'est-à-dire une certaine énumération, pour représenter un champ non déterminé. Ensuite, vous pouvez avoir une fonction de constructeur mkfield 1 vitesse = vitesse val; Mkfield 2 Nom = Nom du nom; mkfield 3 id = id id pour construire des valeurs complètes. .... Je vois, les types ...


@Willness: C'est un exemple simplifié. En fait, j'ai ~ 30 champs, c'est pourquoi je recherche une solution élégante pour que cela facilite le fonctionnement.


Je vois. Vous pouvez appuyer sur peut-être dans les champs, champ de données = vitesse (peut-être flotter) | ... et avoir rien pour représenter les valeurs non continentales.


C'est le problème que j'essayais de résoudre avec une carte . Je veux éviter d'avoir une structure d'enregistrement avec ~ 30 entrées, tout peut-être champ . Avec une carte , je n'ai à vous soucier que des entrées que j'utilisais réellement et que je laisse la sémantique de la recherche s'inquiéter de si l'entrée existe ou non.


Laissez-nous Continuer cette discussion en chat


4 Réponses :


5
votes

Vous pouvez utiliser un gadt,

data Foo :: * -> * where
   FooInt   :: Int   -> Foo Int
   FooFloat :: Float -> Foo Float

data Bar :: * -> * where
   BarInt   :: Int   -> Bar Int
   BarFloat :: Float -> Bar Float

class Pair f b c| f b -> c where
    toPair :: f -> b -> c

instance Pair (Foo Int) (Bar Int) ((Foo Int),Int) where
   toPair a (BarInt b)= (a,b)    


type family FooMap k :: *

type instance FooMap (Foo Int) = Map (Foo Int) (Bar Int)


7 commentaires

C'est vraiment proche de ce dont j'ai besoin. Y a-t-il un moyen de le faire sans diviser FOO en 2 types? J'ai besoin de là pour y avoir un type de foo unifié.


En fait, je ne suis pas sûr que cela fonctionne. En plus d'avoir besoin d'un type de foo unifié, je ne pense pas que je puisse écrire type foobar = carte foo bar puisque la barre n'a pas la sorte *


Je pense que nous pouvons faire ce travail bien pour vous, donnez-moi un peu et je vais essayer de trouver quelque chose


Pouvez-vous expliquer un peu plus sur vos types? Cela pourrait aider à produire une meilleure solution.


FOO va-t-il vraiment être la clé de carte?


FOO est vraiment un type d'énumération de style C / Java pour des champs d'un type de données de message que j'essaie de travailler avec. Chaque champ sera associé à une valeur sur la carte, mais compte tenu du type de champ, je connais déjà le type de valeur unique autorisé. Si le champ est "vitesse" par exemple, dans mon cas, je sais que la seule valeur autorisée doit être un flotteur. Mais si le champ est "NOM", la seule valeur autorisée devrait être byressing. Je souhaite pouvoir écrire un constructeur de sécurité pour la carte afin que ces invariants détiennent et sont vérifiés à l'heure de la compilation.


Cela semble être une étape possible dans la bonne direction, mais ne satisfait pas vraiment aux exigences de ma question. Nommément, je ne peux pas écrire [barint 1, barfloat 2] car leurs types sont barre int et bar flottant respectivement et une liste doit être homogène. J'ai besoin de ma carte pour contenir des touches avec des constructeurs de types foins et foofloat ainsi que les valeurs correctes avec les constructeurs de type Barint et Barfloat < / code>. Je commence à penser que cela pourrait ne pas être possible ...



2
votes

Lorsque j'ai lu cela pour la première fois, j'ai essayé de résoudre le problème de forcer les erreurs de compilation dans les cas requis, mais quelque chose semblait faux. J'ai ensuite essayé une approche avec plusieurs cartes et une fonction de levage, mais quelque chose me harcelait toujours. Cependant, lorsque je me suis rendu compte que essentiellement ce que vous essayez de faire est de créer une forme d'enregistrement extensible, cela me rappelait un paquet très cool que j'avais conscient de quelques mois il y a quelques mois: le paquet de vinyle (disponible sur Hackage ). Cela peut être exactement ou non les effets que vous étiez après, et cela nécessite GHC 7.6, mais voici un exemple adapté de README :

test2Casted :: Rec '["speed" ::: Float]
test2Casted = cast test2


0 commentaires

4
votes

Une version ancienchool à l'aide de dynamique et typable et de fonds de fonds. Pour le garder en sécurité, il vous suffit de ne pas exporter les choses en abstraction-rompant comme le constructeur sm code> et la smkey Smkey code> Typeclass.

{-# LANGUAGE DeriveDataTypeable, MultiParamTypeClasses, FunctionalDependencies, TypeSynonymInstances, FlexibleInstances #-}
module Main where
import qualified Data.Map as M
import Data.Dynamic
import Data.Typeable

data SpecialMap = SM (M.Map String Dynamic)

emptySM = SM (M.empty)

class (Typeable a, Typeable b) => SMKey a b | a -> b

data Speed = Speed deriving Typeable
data Name = Name deriving Typeable
data ID = ID deriving Typeable

instance SMKey Speed Float
instance SMKey Name String
instance SMKey ID Int

insertSM :: SMKey k v => k -> v -> SpecialMap -> SpecialMap
insertSM k v (SM m) = SM (M.insert (show $ typeOf k) (toDyn v) m)

lookupSM :: SMKey k v => k -> SpecialMap -> Maybe v
lookupSM k (SM m) =  fromDynamic =<< M.lookup (show $ typeOf k) m

-- and now lists

newtype SMPair = SMPair {unSMPair :: (String, Dynamic)}
toSMPair :: SMKey k v => k -> v -> SMPair
toSMPair k v = SMPair (show $ typeOf k, toDyn v)

fromPairList :: [SMPair] -> SpecialMap
fromPairList = SM . M.fromList . map unSMPair

{-
*Main> let x = fromPairList [toSMPair Speed 1.2, toSMPair ID 34]
*Main> lookupSM Speed x
Just 1.2
-}


3 commentaires

Cette réponse est arrivée la plus proche de ce dont j'ai besoin. S'il était possible de garder vitesse nom et ID sous un type, ce serait exactement correct.


Avec des types de données, vous pouvez trier de faux qu'ici. Ils seront toujours différents types aussi , mais ils seront également déclarés comme des valeurs uniformes au niveau de la valeur. Je n'ai pas 7,6 sur ma boîte, alors je n'y suis pas allé :-)


De plus, je ne pense pas que cela ajoute quelque chose d'utile à cette solution, ni même réduit le code, tout a dit :-(



-1
votes

Je ferais un type de type opaque avec des getters et des setters.

module Rec (Rec, getSpeed, getName, getID, setSpeed, setName, setID, blank) where

data Rec = R { speed :: Maybe Double; name :: Maybe String; id :: Maybe Int }

getSpeed :: Rec -> Maybe Double
getSpeed = speed

setSpeed :: Double -> Rec -> Rec
setSpeed s r = r { speed = s }

blank = R { speed = Nothing, name = Nothing, id = Nothing }


0 commentaires