Me gustaría representar una wiki (un conjunto de documentos que comprende un gráfico dirigido) en Dhall. Estos documentos se representarán en HTML y me gustaría evitar que se generen enlaces rotos. A mi entender, esto podría lograrse haciendo gráficos no válidos (gráficos con enlaces a nodos no existentes) no representables a través del sistema de tipos o escribiendo una función para devolver una lista de errores en cualquier gráfico posible (por ejemplo, "En un gráfico posible X, el Nodo A contiene un enlace a un Nodo B inexistente ").
Una representación ingenua de la lista de adyacencia podría verse así:
let Node : Type = {
id: Text,
neighbors: List Text
}
let Graph : Type = List Node
let example : Graph = [
{ id = "a", neighbors = ["b"] }
]
in example
Como este ejemplo hace evidente, este tipo admite valores que no corresponden a gráficos válidos (no hay ningún nodo con una identificación de "b", pero el nodo con la identificación "a" estipula un vecino con la identificación "b"). Además, no es posible generar una lista de estos problemas doblando los vecinos de cada Nodo, porque Dhall no admite la comparación de cadenas por diseño.
¿Hay alguna representación que permita el cálculo de una lista de enlaces rotos o la exclusión de enlaces rotos a través del sistema de tipos?
ACTUALIZACIÓN: Acabo de descubrir que los productos naturales son comparables en Dhall. Así que supongo que se podría escribir una función para identificar cualquier borde no válido ("enlaces rotos") y usos duplicados de un identificador si los identificadores fueran naturales.
Sin embargo, la pregunta original es si se puede definir un tipo de Gráfico.
fuente
Respuestas:
Sí, puede modelar un gráfico de tipo seguro, dirigido, posiblemente cíclico, en Dhall, así:
Esta representación garantiza la ausencia de bordes rotos.
También convertí esta respuesta en un paquete que puedes usar:
Editar: Aquí hay recursos relevantes y explicaciones adicionales que pueden ayudar a iluminar lo que está sucediendo:
Primero, comience desde el siguiente tipo de Haskell para un árbol :
Puede pensar en este tipo como una estructura de datos perezosa y potencialmente infinita que representa lo que obtendría si siguiera visitando vecinos.
Ahora, supongamos que la
Tree
representación anterior en realidad es nuestraGraph
simplemente cambiando el nombre del tipo de datos aGraph
:... pero incluso si quisiéramos usar este tipo, no tenemos una forma de modelar directamente ese tipo en Dhall porque el lenguaje Dhall no proporciona soporte incorporado para estructuras de datos recursivas. ¿Asi que que hacemos?
Afortunadamente, en realidad hay una manera de integrar estructuras de datos recursivas y funciones recursivas en un lenguaje no recursivo como Dhall. De hecho, hay dos maneras!
Lo primero que leí que me presentó este truco fue el siguiente borrador de Wadler:
... pero puedo resumir la idea básica usando los siguientes dos tipos de Haskell:
... y:
La forma
LFix
y elGFix
trabajo es que puede darles "una capa" de su tipo recursivo o "corecursive" deseado (es decir, elf
) y luego le dan algo tan poderoso como el tipo deseado sin necesidad de soporte de idioma para la recursión o corecursion .Usemos las listas como ejemplo. Podemos modelar "una capa" de una lista usando el siguiente
ListF
tipo:Compare esa definición con la forma en que normalmente definiríamos un
OrdinaryList
uso de una definición de tipo de datos recursiva ordinaria:La principal diferencia es que
ListF
toma un parámetro de tipo adicional (next
), que usamos como marcador de posición para todas las ocurrencias recursivas / corecursive del tipo.Ahora, equipados con
ListF
, podemos definir listas recursivas y corecursive como esta:... dónde:
List
es una lista recursiva implementada sin soporte de idioma para la recursividadCoList
es una lista corecursive implementada sin soporte de lenguaje para corecursionAmbos tipos son equivalentes a ("isomorfo a")
[]
, lo que significa que:List
y[]
CoList
y[]
¡Probemos eso definiendo esas funciones de conversión!
Entonces, el primer paso para implementar el tipo Dhall fue convertir el
Graph
tipo recursivo :... a la representación co-recursiva equivalente:
... aunque para simplificar un poco los tipos, creo que es más fácil especializarse
GFix
en el caso dondef = GraphF
:Haskell no tiene registros anónimos como Dhall, pero si los tuviera, podríamos simplificar aún más el tipo al incluir la definición de
GraphF
:Ahora esto comienza a parecerse al tipo Dhall para a
Graph
, especialmente si lo reemplazamosx
connode
:Sin embargo, todavía hay una última parte difícil, que es cómo traducir el
ExistentialQuantification
de Haskell a Dhall. Resulta que siempre puedes traducir la cuantificación existencial a la cuantificación universal (es decirforall
) usando la siguiente equivalencia:Creo que esto se llama "skolemization"
Para más detalles, ver:
... y ese truco final te da el tipo Dhall:
... donde
forall (Graph : Type)
juega el mismo papel queforall x
en la fórmula anterior yforall (Node : Type)
juega el mismo papel queforall y
en la fórmula anterior.fuente