Eu gostaria de representar um wiki (um conjunto de documentos que inclui um gráfico direcionado) em Dhall. Esses documentos serão renderizados em HTML e eu gostaria de impedir que links quebrados sejam gerados. Na minha opinião, isso pode ser feito tornando gráficos inválidos (gráficos com links para nós inexistentes) não representáveis através do sistema de tipos ou escrevendo uma função para retornar uma lista de erros em qualquer gráfico possível (por exemplo, "Em gráfico possível" X, Nó A contém um link para um Nó B inexistente ").
Uma representação ingênua da lista de adjacências pode se parecer com isso:
let Node : Type = {
id: Text,
neighbors: List Text
}
let Graph : Type = List Node
let example : Graph = [
{ id = "a", neighbors = ["b"] }
]
in example
Como este exemplo evidencia, esse tipo admite valores que não correspondem a gráficos válidos (não há nó com o ID "b", mas o nó com o ID "a" estipula um vizinho com o ID "b"). Além disso, não é possível gerar uma lista desses problemas dobrando os vizinhos de cada Nó, porque o Dhall não suporta comparação de cadeias por design.
Existe alguma representação que permita o cálculo de uma lista de links quebrados ou a exclusão de links quebrados através do sistema de tipos?
ATUALIZAÇÃO: Acabei de descobrir que os Naturals são comparáveis em Dhall. Portanto, suponho que uma função possa ser escrita para identificar quaisquer arestas inválidas ("links quebrados") e duplicar os usos de um identificador se os identificadores fossem Naturals.
A questão original, no entanto, sobre se um tipo de gráfico pode ser definido, permanece.
fonte
Respostas:
Sim, você pode modelar um gráfico de tipo seguro, direcionado e possivelmente cíclico no Dhall, assim:
Essa representação garante a ausência de arestas quebradas.
Também transformei esta resposta em um pacote que você pode usar:
Editar: Aqui estão recursos relevantes e explicações adicionais que podem ajudar a esclarecer o que está acontecendo:
Primeiro, comece pelo seguinte tipo de Haskell para uma árvore :
Você pode pensar nesse tipo como uma estrutura de dados lenta e potencialmente infinita, representando o que você obteria se apenas continuasse visitando vizinhos.
Agora, vamos fingir que a
Tree
representação acima é realmente nossaGraph
apenas renomeando o tipo de dados paraGraph
:... mas mesmo que desejássemos usar esse tipo, não temos como modelar diretamente esse tipo no Dhall porque a linguagem Dhall não fornece suporte interno para estruturas de dados recursivas. Então, o que fazemos?
Felizmente, existe realmente uma maneira de incorporar estruturas de dados recursivas e funções recursivas em uma linguagem não recursiva como Dhall. De fato, existem duas maneiras!
A primeira coisa que li que me apresentou a esse truque foi o seguinte rascunho de Wadler:
... mas posso resumir a ideia básica usando os dois seguintes tipos de Haskell:
... e:
A maneira que
LFix
eGFix
trabalho é que você pode dar-lhes "uma camada" de sua recursiva desejado ou tipo "corecursive" (ou seja, of
) e, em seguida, dar-lhe algo que é tão poderoso como o tipo desejado sem a necessidade de suporte de idioma para a recursividade ou corecursion .Vamos usar listas como um exemplo. Podemos modelar "uma camada" de uma lista usando o seguinte
ListF
tipo:Compare essa definição com a forma como normalmente definiríamos uma
OrdinaryList
definição de tipo de dados recursivo comum:A principal diferença é que
ListF
leva um parâmetro de tipo extra (next
), que usamos como espaço reservado para todas as ocorrências recursivas / corecursivas do tipo.Agora, equipado com
ListF
, podemos definir listas recursivas e corecursivas como esta:... Onde:
List
é uma lista recursiva implementada sem suporte ao idioma para recursãoCoList
é uma lista corecursiva implementada sem suporte ao idioma para corecursãoAmbos os tipos são equivalentes a ("isomórfico para")
[]
, significando que:List
e[]
CoList
e[]
Vamos provar que, definindo essas funções de conversão!
Portanto, o primeiro passo na implementação do tipo Dhall foi converter o
Graph
tipo recursivo :... à representação co-recursiva equivalente:
... embora para simplificar um pouco os tipos, acho mais fácil me especializar
GFix
no caso em quef = GraphF
:Haskell não possui registros anônimos como Dhall, mas, se o tivesse, poderíamos simplificar ainda mais o tipo ao incluir a definição de
GraphF
:Agora, isso está começando a se parecer com o tipo Dhall para a
Graph
, especialmente se substituirmosx
pornode
:No entanto, ainda há uma última parte complicada, que é como traduzir o
ExistentialQuantification
de Haskell para Dhall. Acontece que você sempre pode converter quantificação existencial em quantificação universal (ou sejaforall
) usando a seguinte equivalência:Eu acredito que isso se chama "skolemization"
Para mais detalhes, consulte:
... e esse truque final fornece o tipo de Dhall:
... onde
forall (Graph : Type)
desempenha o mesmo papel queforall x
na fórmula anterior eforall (Node : Type)
desempenha o mesmo papel queforall y
na fórmula anterior.fonte