1

Disons que j'ai un HAA représentant une sorte de structure arborescente:Comment est-ce que j'utilise le système de type de Haskell pour appliquer la correction tout en étant capable de faire correspondre le modèle?

data Tree = ANode (Maybe Tree) (Maybe Tree) AValType 
      | BNode (Maybe Tree) (Maybe Tree) BValType 
      | CNode (Maybe Tree) (Maybe Tree) CValType 

Pour autant que je sais qu'il n'y a aucun moyen de correspondance de modèles contre les constructeurs de type (ou les fonctions correspondantes se serait pas un type?) mais je voudrais toujours utiliser le système de type compile-time pour éliminer la possibilité de retourner ou d'analyser le mauvais 'type' du noeud Tree. Par exemple, il se peut que CNode ne puisse être que des parents d'ANODE. Je pourrais avoir

parseANode :: Parser (Maybe Tree) 

en fonction de l'analyse syntaxique parsec qui s'est utilisé dans le cadre de mon analyseur Nœuds-C:

parseCNode :: Parser (Maybe Tree) 
parseCNode = try (
        string "<CNode>" >> 
        parseANode >>= \maybeanodel -> 
        parseANode >>= \maybeanoder -> 
        parseCValType >>= \cval -> 
        string "</CNode>" 
        return (Just (CNode maybeanodel maybeanoder cval)) 
      ) <|> return Nothing 

Selon le système de type, parseANode pourrait finir par revenir un Maybe Nœuds-C, un bnode Peut-être , ou un ANODE peut-être, mais je veux vraiment m'assurer qu'il ne renvoie qu'un ANODE Peut-être. Notez que ce n'est pas une valeur de schéma de données ou d'exécution - vérifiez ce que je veux faire - Je suis en train d'essayer de vérifier la validité de l'analyseur que j'ai écrit pour un schéma d'arbre particulier. IOW, je n'essaie pas de vérifier les données analysées pour l'exactitude du schéma, ce que j'essaie vraiment de faire est de vérifier mon analyseur pour l'exactitude du schéma - Je voudrais juste m'assurer que je ne bâcle pas parseANode un jour pour renvoyer autre chose qu'une valeur ANode.

J'espérais que peut-être si j'apparié contre le constructeur de valeur dans la variable de liaison, que le type inférences serait comprendre ce que je voulais dire:

parseCNode :: Parser (Maybe Tree) 
parseCNode = try (
        string "<CNode>" >> 
        parseANode >>= \(Maybe (ANode left right avall)) -> 
        parseANode >>= \(Maybe (ANode left right avalr)) -> 
        parseCValType >>= \cval -> 
        string "</CNode>" 
        return (Just (CNode (Maybe (ANode left right avall)) (Maybe (ANode left right avalr)) cval)) 
      ) <|> return Nothing 

Mais cela a beaucoup de problèmes, et non la moins que ce parseANode n'est plus libre de retourner Nothing. Et cela ne fonctionne pas de toute façon - il semble que cette variable de liaison est traitée comme une correspondance de modèle et l'exécution se plaint de la correspondance de modèle non exhaustive lorsque parseANode renvoie Nothing ou Maybe BNode ou quelque chose.

je pouvais faire quelque chose dans ce sens:

data ANode = ANode (Maybe BNode) (Maybe BNode) AValType 
data BNode = BNode (Maybe CNode) (Maybe CNode) BValType 
data CNode = CNode (Maybe ANode) (Maybe ANode) CValType 

mais ce genre de suce, car il suppose que la contrainte est appliquée à tous les nœuds - je ne pourrais pas être intéressé à le faire - il pourrait en effet être juste CNodes qui ne peuvent être que des ANodes parentales. Donc je suppose que je pourrais le faire:

data AnyNode = AnyANode ANode | AnyBNode BNode | AnyCNode CNode 

data ANode = ANode (Maybe AnyNode) (Maybe AnyNode) AValType 
data BNode = BNode (Maybe AnyNode) (Maybe AnyNode) BValType 
data CNode = CNode (Maybe ANode) (Maybe ANode) CValType 

mais cela rend beaucoup plus difficile à motif match contre son * Noeud - en fait, il est impossible parce qu'ils sont juste complètement types distincts. Je pourrais faire une classe de types où je voulais motif match, je suppose que

class Node t where 
    matchingFunc :: t -> Bool 

instance Node ANode where 
    matchingFunc (ANode left right val) = testA val 

instance Node BNode where 
    matchingFunc (BNode left right val) = val == refBVal 

instance Node CNode where 
    matchingFunc (CNode left right val) = doSomethingWithACValAndReturnABool val 

En tout cas, cela semble juste sorte de désordre. Quelqu'un peut-il penser à une façon plus succincte de le faire?

+0

Hmm ... vous pouvez dire que j'apprends encore à 'penser en Haskell'. La correspondance de modèle est destinée à la correspondance dans un type. Les classes de type permettent de faire correspondre le modèle intra-type à la correspondance de modèle inter-type. J'ai dactylographié cette question avant que je l'ai compris. Quoi qu'il en soit, je voudrais toujours avoir des nouvelles de tous ceux qui peuvent résoudre cela plus élégamment ... –

+0

A en juger par votre dernier exemple de code, il semble que ce que vous voulez faire soit compliqué, donc je ne serais pas surpris si un programme pour atteindre ça (quoi que ce soit) est compliqué ... –

+0

En outre, le deuxième code à partir du dernier a quelque chose de mal parce que chacun des constructeurs ANode, BNode et CNode est déclaré deux fois. –

Répondre

4

Je ne comprends pas votre objection à votre solution finale. Vous pouvez toujours match de modèle contre AnyNode s, comme ceci:

f (AnyANode (ANode x y z)) = ... 

Il est un peu plus bavard, mais je pense qu'il a les propriétés techniques que vous voulez.

+0

Oui - ça marche ... quand j'ai commencé à taper cette question, je n'avais pas pensé si loin. À ce stade, je suis plus intéressé à voir s'il y a une solution plus compacte? Expressif? –

+2

Il ne semble pas expressif est ce que vous cherchez. Je peux comprendre votre désir de concision. Mais je suis d'accord avec Luqui, votre solution finale semble résoudre votre problème, d'une manière très simple. Comme un avantage supplémentaire, quand on lit le code, il est clair les propriétés que vous essayez d'exprimer dans votre structure de données. - Je pense que c'est une propriété importante à considérer. – mayahustle

+0

Encore une chose, je pense qu'un problème plus intéressant serait l'application de propriétés à une plus grande profondeur du parent. Par exemple, en ce moment, vous appliquez la relation parent-enfant directe.Mais que faire si vous ne voulez jamais qu'un nœud C contienne un BNode dans son sous-arbre? À l'heure actuelle, la hiérarchie permet que CNode-> ANode-> BNode .. – mayahustle

4

Je voudrais encore utiliser le système de type compilation pour éliminer la possibilité de retourner ou de l'analyse du mauvais « type » du nœud de l'arbre

Cela ressemble à un cas d'utilisation pour GADTs.

{-# LANGUAGE GADTs, EmptyDataDecls #-} 
data ATag 
data BTag 
data CTag 

data Tree t where 
    ANode :: Maybe (Tree t) -> Maybe (Tree t) -> AValType -> Tree ATag 
    BNode :: Maybe (Tree t) -> Maybe (Tree t) -> BValType -> Tree BTag 
    CNode :: Maybe (Tree t) -> Maybe (Tree t) -> CValType -> Tree CTag 

Maintenant, vous pouvez utiliser Tree t lorsque vous ne se soucient pas du type de noeud, ou Tree ATag quand vous faites.

+1

Je crois que les GADT sont un peu un gros marteau pour ce problème. Un arbre non restreint est maintenant un arbre existentiel: 'existe t. Arbre t', qui n'est pas facilement exprimé chez Haskell. – luqui

+2

Je pense que vous devez inclure 't' ou une balise pour les paramètres' Maybe Tree' dans cet exemple. –

+0

@John: En fait, je pense que cela rend cette solution problématique - l'arbre doit maintenant encoder la balise pour tous ses sous-arbres, ce qui serait ... désordonné. –

Questions connexes