Je voudrais représenter un wiki (un ensemble de documents comprenant un graphique dirigé) dans Dhall. Ces documents seront rendus au format HTML, et j'aimerais empêcher la création de liens rompus. Comme je le vois, cela pourrait être accompli soit en rendant les graphiques invalides (graphiques avec des liens vers des nœuds inexistants) non représentables via le système de type ou en écrivant une fonction pour renvoyer une liste d'erreurs dans tout graphique possible (par exemple, "Dans un graphique possible X, le nœud A contient un lien vers un nœud B inexistant ").
Une représentation de liste d'adjacence naïve pourrait ressembler à ceci:
let Node : Type = {
id: Text,
neighbors: List Text
}
let Graph : Type = List Node
let example : Graph = [
{ id = "a", neighbors = ["b"] }
]
in example
Comme cet exemple le montre clairement, ce type admet des valeurs qui ne correspondent pas à des graphiques valides (il n'y a pas de nœud avec un identifiant "b", mais le nœud avec l'identifiant "a" stipule un voisin avec l'identifiant "b"). De plus, il n'est pas possible de générer une liste de ces problèmes en repliant les voisins de chaque nœud, car Dhall ne prend pas en charge la comparaison de chaînes par conception.
Existe-t-il une représentation qui permettrait soit le calcul d'une liste de liens rompus, soit l'exclusion de liens rompus via le système de types?
MISE À JOUR: Je viens de découvrir que les produits naturels sont comparables dans Dhall. Je suppose donc qu'une fonction pourrait être écrite pour identifier les bords non valides ("liens rompus") et les utilisations en double d'un identifiant si les identifiants étaient des produits naturels.
La question initiale, cependant, de savoir si un type de graphique peut être défini, demeure.
la source
Réponses:
Oui, vous pouvez modéliser un graphique de type sécurisé, dirigé, éventuellement cyclique dans Dhall, comme ceci:
Cette représentation garantit l'absence de bords cassés.
J'ai également transformé cette réponse en un package que vous pouvez utiliser:
Modifier: Voici des ressources pertinentes et des explications supplémentaires qui peuvent aider à éclairer ce qui se passe:
Commencez d'abord par le type Haskell suivant pour un arbre :
Vous pouvez considérer ce type comme une structure de données paresseuse et potentiellement infinie représentant ce que vous obtiendriez si vous continuiez à visiter des voisins.
Maintenant, supposons que la
Tree
représentation ci-dessus est réellement la notreGraph
en renommant simplement le type de données enGraph
:... mais même si nous voulions utiliser ce type, nous n'avons pas de moyen de modéliser directement ce type dans Dhall parce que le langage Dhall ne fournit pas de support intégré pour les structures de données récursives. Alors que faisons-nous?
Heureusement, il existe en fait un moyen d'incorporer des structures de données récursives et des fonctions récursives dans un langage non récursif comme Dhall. En fait, il y a deux façons!
La première chose que j'ai lue qui m'a présenté cette astuce a été le brouillon suivant de Wadler:
... mais je peux résumer l'idée de base en utilisant les deux types Haskell suivants:
... et:
La façon dont cela
LFix
et leGFix
travail sont que vous pouvez leur donner "une couche" de votre type récursif ou "corécursif" souhaité (c'est-à-dire lef
) et ils vous donnent alors quelque chose qui est aussi puissant que le type souhaité sans nécessiter le support de la langue pour la récursivité ou la corécursion .Prenons l'exemple des listes. Nous pouvons modéliser "une couche" d'une liste en utilisant le
ListF
type suivant :Comparez cette définition à la façon dont nous définirions normalement une
OrdinaryList
définition de type de données récursive ordinaire:La principale différence est qu'il
ListF
prend un paramètre de type supplémentaire (next
), que nous utilisons comme espace réservé pour toutes les occurrences récursives / corécursives du type.Maintenant, équipé de
ListF
, nous pouvons définir des listes récursives et corécursives comme ceci:... où:
List
est une liste récursive implémentée sans prise en charge linguistique pour la récursivitéCoList
est une liste corecursive implémentée sans prise en charge linguistique pour la corecursionCes deux types sont équivalents à ("isomorphic to")
[]
, ce qui signifie que:List
et[]
CoList
et[]
Prouvons cela en définissant ces fonctions de conversion!
La première étape de la mise en œuvre du type Dhall a donc consisté à convertir le
Graph
type récursif :... à la représentation co-récursive équivalente:
... bien que pour simplifier un peu les types, je trouve qu'il est plus facile de se spécialiser
GFix
dans le cas oùf = GraphF
:Haskell n'a pas d'enregistrements anonymes comme Dhall, mais si c'était le cas, nous pourrions simplifier davantage le type en insérant la définition de
GraphF
:Maintenant, cela commence à ressembler au type Dhall pour a
Graph
, surtout si nous le remplaçonsx
parnode
:Cependant, il y a encore une dernière partie délicate, qui est de savoir comment traduire
ExistentialQuantification
Haskell en Dhall. Il s'avère que vous pouvez toujours traduire la quantification existentielle en quantification universelle (c'est-à-direforall
) en utilisant l'équivalence suivante:Je crois que cela s'appelle la "skolémisation"
Pour plus de détails, voir:
... et cette dernière astuce vous donne le type Dhall:
... où
forall (Graph : Type)
joue le même rôle queforall x
dans la formule précédente etforall (Node : Type)
joue le même rôle queforall y
dans la formule précédente.la source