Chciałbym reprezentować wiki (zestaw dokumentów zawierający ukierunkowany wykres) w Dhall. Te dokumenty zostaną wyrenderowane w formacie HTML i chciałbym zapobiec generowaniu niedziałających linków. Moim zdaniem można to osiągnąć albo przez to, że nieprawidłowe wykresy (wykresy z linkami do nieistniejących węzłów) nie będą reprezentowalne przez system typów, albo przez napisanie funkcji zwracającej listę błędów na dowolnym możliwym wykresie (np. „Na możliwym wykresie” X, Węzeł A zawiera łącze do nieistniejącego Węzła B ”).
Naiwna reprezentacja listy przylegania może wyglądać mniej więcej tak:
let Node : Type = {
id: Text,
neighbors: List Text
}
let Graph : Type = List Node
let example : Graph = [
{ id = "a", neighbors = ["b"] }
]
in example
Jak pokazuje ten przykład, ten typ dopuszcza wartości, które nie odpowiadają prawidłowym wykresom (nie ma węzła o identyfikatorze „b”, ale węzeł o identyfikatorze „a” określa sąsiada o identyfikatorze „b”). Co więcej, nie jest możliwe wygenerowanie listy tych problemów poprzez złożenie sąsiadów każdego węzła, ponieważ Dhall nie obsługuje porównania ciągów według projektu.
Czy istnieje jakakolwiek reprezentacja, która pozwoliłaby na obliczenie listy uszkodzonych łączy lub wykluczenie uszkodzonych linków przez system typów?
AKTUALIZACJA: Właśnie odkryłem, że Naturals są porównywalne w Dhall. Podejrzewam więc, że można by napisać funkcję identyfikującą wszelkie nieprawidłowe krawędzie („zepsute linki”) i powielającą użycie identyfikatora, gdyby były to Naturals.
Pozostaje jednak pierwotne pytanie, czy można zdefiniować typ wykresu.
źródło
Odpowiedzi:
Tak, możesz modelować wykres typu bezpieczny, ukierunkowany, być może cykliczny w Dhall, w następujący sposób:
Ta reprezentacja gwarantuje brak złamanych krawędzi.
Zmieniłem również tę odpowiedź w pakiet, którego możesz użyć:
Edycja: Oto odpowiednie zasoby i dodatkowe wyjaśnienia, które mogą pomóc wyjaśnić, co się dzieje:
Najpierw zacznij od następującego typu Haskell dla drzewa :
Możesz myśleć o tym typie jako o leniwej i potencjalnie nieskończonej strukturze danych reprezentującej to, co byś otrzymał, gdybyś tylko odwiedzał sąsiadów.
Teraz udawajmy, że powyższa
Tree
reprezentacja faktycznie jest naszaGraph
, zmieniając po prostu nazwę typu danych naGraph
:... ale nawet gdybyśmy chcieli użyć tego typu, nie mamy możliwości bezpośredniego modelowania tego typu w Dhall, ponieważ język Dhall nie zapewnia wbudowanej obsługi struktur danych rekurencyjnych. Więc co robimy?
Na szczęście istnieje sposób na osadzenie struktur danych rekurencyjnych i funkcji rekurencyjnych w nierekurencyjnym języku, takim jak Dhall. W rzeczywistości istnieją dwa sposoby!
Pierwszą rzeczą, którą przeczytałem, która zapoznała mnie z tą sztuczką, był następujący szkic posta Wadlera:
... ale mogę podsumować podstawowy pomysł, używając następujących dwóch typów Haskell:
... i:
Sposób, że
LFix
iGFix
praca jest, że można dać im „jedną warstwę” żądanej rekursywne lub „corecursive” typ (tjf
) i następnie daje coś, co jest tak potężny jak żądanego typu bez konieczności wsparcia językowego dla rekursji lub corecursion .Użyjmy list jako przykładu. Możemy modelować „jedną warstwę” listy, używając następującego
ListF
typu:Porównaj tę definicję z tym, jak normalnie zdefiniowalibyśmy
OrdinaryList
zwykłą rekurencyjną definicję typu danych:Główną różnicą jest to, że
ListF
pobiera jeden dodatkowy parametr type (next
), którego używamy jako symbolu zastępczego dla wszystkich rekurencyjnych / corecursive wystąpień tego typu.Teraz, wyposażeni w
ListF
, możemy zdefiniować listy rekurencyjne i współbieżne w następujący sposób:... gdzie:
List
to lista rekurencyjna zaimplementowana bez obsługi języka dla rekurencjiCoList
to lista Corecursive zaimplementowana bez obsługi języka dla CorecursionOba te typy są równoważne („izomorficzny z”)
[]
, co oznacza, że:List
i[]
CoList
i[]
Udowodnijmy to, definiując funkcje konwersji!
Pierwszym krokiem w implementacji typu Dhall była konwersja
Graph
typu rekurencyjnego :... do równoważnej reprezentacji ko-rekurencyjnej:
... chociaż trochę upraszczając typy, łatwiej jest specjalizować
GFix
się w przypadku, w którymf = GraphF
:Haskell nie ma anonimowych rekordów takich jak Dhall, ale gdyby tak było, moglibyśmy jeszcze bardziej uprościć typ, wprowadzając definicję
GraphF
:Teraz zaczyna to wyglądać typu Dhall dotyczący
Graph
zwłaszcza jeśli zastąpimyx
znode
:Jednak jest jeszcze jedna trudna część, która polega na przetłumaczeniu
ExistentialQuantification
Haskella na Dhall. Okazuje się, że zawsze można przełożyć kwantyfikację egzystencjalną na kwantyfikację uniwersalną (tj.forall
), Stosując następującą równoważność:Wierzę, że to się nazywa „skolemizacja”
Aby uzyskać więcej informacji, zobacz:
... a ta ostatnia sztuczka daje typ Dhalla:
... gdzie
forall (Graph : Type)
odgrywa taką samą rolę jakforall x
w poprzedniej formule iforall (Node : Type)
odgrywa taką samą rolę jakforall y
w poprzedniej formule.źródło