क्या मान्य ग्राफ़ के प्रकार को Dhall में एन्कोड किया जा सकता है?


10

मैं ढल्ल में एक विकी (निर्देशित ग्राफ सहित दस्तावेजों का एक सेट) का प्रतिनिधित्व करना चाहता हूं। ये दस्तावेज़ HTML को प्रदान किए जाएंगे, और मैं चाहता हूं कि टूटे हुए लिंक को कभी उत्पन्न होने से रोका जाए। जैसा कि मैं इसे देखता हूं, इसे या तो अमान्य ग्राफ़ (गैर-मौजूद नोड्स के लिंक के साथ ग्राफ़) के द्वारा पूरा किया जा सकता है, टाइप सिस्टम के माध्यम से अप्राप्य या किसी भी संभावित ग्राफ़ में त्रुटियों की सूची को वापस करने के लिए फ़ंक्शन लिखकर (जैसे "संभव ग्राफ़ में" X, Node A में एक गैर-मौजूद Node B "का लिंक है))।

एक भोली बगल सूची प्रतिनिधित्व कुछ इस तरह लग सकता है:

let Node : Type = {
    id: Text,
    neighbors: List Text
}
let Graph : Type = List Node
let example : Graph = [
    { id = "a", neighbors = ["b"] }
]
in example

जैसा कि यह उदाहरण स्पष्ट करता है, यह प्रकार उन मानों को स्वीकार करता है जो वैध रेखांकन के अनुरूप नहीं हैं ("बी" की आईडी के साथ कोई नोड नहीं है, लेकिन आईडी के साथ नोड "आईडी" बी "के साथ पड़ोसी को निर्धारित करता है)। इसके अलावा, प्रत्येक नोड के पड़ोसियों पर फोल्ड करके इन मुद्दों की एक सूची तैयार करना संभव नहीं है, क्योंकि Dhall डिजाइन के साथ स्ट्रिंग तुलना का समर्थन नहीं करता है।

क्या कोई प्रतिनिधित्व है जो या तो टूटी हुई लिंक की एक सूची की गणना या प्रकार प्रणाली के माध्यम से टूटे हुए लिंक के बहिष्कार की अनुमति देगा?

UPDATE: मैंने अभी-अभी पता लगाया है कि नैचुरल Dhall में तुलनीय हैं। इसलिए मुझे लगता है कि यदि कोई पहचानकर्ता नेचुरल थे, तो किसी भी अमान्य किनारों ("टूटे लिंक") और किसी पहचानकर्ता के डुप्लिकेट उपयोग की पहचान करने के लिए एक फ़ंक्शन लिखा जा सकता है।

हालांकि, मूल प्रश्न, क्या ग्राफ़ प्रकार को परिभाषित किया जा सकता है, बनी हुई है।


इसके बजाय किनारों की सूची के रूप में ग्राफ का प्रतिनिधित्व करें। मौजूदा किनारों से नोड्स का अनुमान लगाया जा सकता है। प्रत्येक किनारे में एक स्रोत नोड और एक गंतव्य नोड शामिल होगा, लेकिन डिस्कनेक्ट नोड्स को समायोजित करने के लिए, गंतव्य वैकल्पिक हो सकता है।
चेप्टर

जवाबों:


18

हां, आप इस तरह से एक सुरक्षित, निर्देशित, संभवतः-चक्रीय, ग्राफ में मॉडल बना सकते हैं:

let List/map =
      https://prelude.dhall-lang.org/v14.0.0/List/map sha256:dd845ffb4568d40327f2a817eb42d1c6138b929ca758d50bc33112ef3c885680

let Graph
    : Type
    =     forall (Graph : Type)
      ->  forall  ( MakeGraph
                  :     forall (Node : Type)
                    ->  Node
                    ->  (Node -> { id : Text, neighbors : List Node })
                    ->  Graph
                  )
      ->  Graph

let MakeGraph
    :     forall (Node : Type)
      ->  Node
      ->  (Node -> { id : Text, neighbors : List Node })
      ->  Graph
    =     \(Node : Type)
      ->  \(current : Node)
      ->  \(step : Node -> { id : Text, neighbors : List Node })
      ->  \(Graph : Type)
      ->  \ ( MakeGraph
            :     forall (Node : Type)
              ->  Node
              ->  (Node -> { id : Text, neighbors : List Node })
              ->  Graph
            )
      ->  MakeGraph Node current step

let -- Get `Text` label for the current node of a Graph
    id
    : Graph -> Text
    =     \(graph : Graph)
      ->  graph
            Text
            (     \(Node : Type)
              ->  \(current : Node)
              ->  \(step : Node -> { id : Text, neighbors : List Node })
              ->  (step current).id
            )

let -- Get all neighbors of the current node
    neighbors
    : Graph -> List Graph
    =     \(graph : Graph)
      ->  graph
            (List Graph)
            (     \(Node : Type)
              ->  \(current : Node)
              ->  \(step : Node -> { id : Text, neighbors : List Node })
              ->  let neighborNodes
                      : List Node
                      = (step current).neighbors

                  let nodeToGraph
                      : Node -> Graph
                      =     \(node : Node)
                        ->  \(Graph : Type)
                        ->  \ ( MakeGraph
                              :     forall (Node : Type)
                                ->  forall (current : Node)
                                ->  forall  ( step
                                            :     Node
                                              ->  { id : Text
                                                  , neighbors : List Node
                                                  }
                                            )
                                ->  Graph
                              )
                        ->  MakeGraph Node node step

                  in  List/map Node Graph nodeToGraph neighborNodes
            )

let {- Example node type for a graph with three nodes

           For your Wiki, replace this with a type with one alternative per document
        -}
    Node =
      < Node0 | Node1 | Node2 >

let {- Example graph with the following nodes and edges between them:

                       Node0 ↔ Node1
                         ↓
                       Node2
                         ↺

           The starting node is Node0
        -}
    example
    : Graph
    = let step =
                \(node : Node)
            ->  merge
                  { Node0 = { id = "0", neighbors = [ Node.Node1, Node.Node2 ] }
                  , Node1 = { id = "1", neighbors = [ Node.Node0 ] }
                  , Node2 = { id = "2", neighbors = [ Node.Node2 ] }
                  }
                  node

      in  MakeGraph Node Node.Node0 step

in  assert : List/map Graph Text id (neighbors example) === [ "1", "2" ]

यह प्रतिनिधित्व टूटे हुए किनारों की अनुपस्थिति की गारंटी देता है।

मैंने इस उत्तर को एक पैकेज में बदल दिया जिसका आप उपयोग कर सकते हैं:

संपादित करें: यहां प्रासंगिक संसाधन और अतिरिक्त स्पष्टीकरण दिए गए हैं जो कि क्या चल रहा है, इसे रोशन करने में मदद कर सकते हैं:

सबसे पहले, एक पेड़ के लिए निम्नलिखित हास्केल प्रकार से शुरू करें :

data Tree a = Node { id :: a, neighbors :: [ Tree a ] }

आप इस प्रकार के एक आलसी और संभावित रूप से अनंत डेटा संरचना के रूप में सोच सकते हैं कि यदि आप सिर्फ पड़ोसियों के पास जाते रहे तो आपको क्या मिलेगा।

अब, आइए दिखाते हैं कि उपर्युक्त Treeप्रतिनिधित्व वास्तव मेंGraph सिर्फ डेटाटाइप का नाम बदलकर हमारा है Graph:

data Graph a = Node { id :: a, neighbors :: [ Graph a ] }

... लेकिन अगर हम इस प्रकार का उपयोग करना चाहते हैं, तो हमारे पास सीधे उस प्रकार का मॉडल बनाने का कोई तरीका नहीं है, क्योंकि Dhall भाषा पुनरावर्ती डेटा संरचनाओं के लिए अंतर्निहित समर्थन प्रदान नहीं करती है। तो हम क्या करे?

सौभाग्य से, वास्तव में Dhall जैसी गैर-पुनरावर्ती भाषा में पुनरावर्ती डेटा संरचनाओं और पुनरावर्ती कार्यों को एम्बेड करने का एक तरीका है। वास्तव में, दो तरीके हैं!

पहली बात जो मैंने पढ़ी, उसने मुझे इस ट्रिक से परिचित कराया, वह था वडलर का निम्नलिखित ड्राफ्ट पोस्ट:

... लेकिन मैं निम्नलिखित दो हास्केल प्रकारों का उपयोग करके मूल विचार को संक्षेप में बता सकता हूं:

{-# LANGUAGE RankNTypes #-}

-- LFix is short for "Least fixed point"
newtype LFix f = LFix (forall x . (f x -> x) -> x)

... तथा:

{-# LANGUAGE ExistentialQuantification #-}

-- GFix is short for "Greatest fixed point"
data GFix f = forall x . GFix x (x -> f x)

जिस तरह से LFixऔर GFixकाम यह है कि आप उन्हें अपने इच्छित पुनरावर्ती या "corecursive" प्रकार (यानी f) की "एक परत" दे सकते हैं और वे आपको कुछ ऐसा प्रदान करते हैं जो पुनरावृत्ति या corecursion के लिए भाषा समर्थन की आवश्यकता के बिना वांछित प्रकार है। ।

उदाहरण के रूप में सूचियों का उपयोग करते हैं। हम निम्न ListFप्रकार का उपयोग करके सूची की "एक परत" को मॉडल कर सकते हैं :

-- `ListF` is short for "List functor"
data ListF a next = Nil | Cons a next

उस परिभाषा की तुलना करें कि हम आम तौर पर OrdinaryListएक सामान्य पुनरावर्ती डेटाटाइप परिभाषा का उपयोग करके कैसे परिभाषित करेंगे :

data OrdinaryList a = Nil | Cons a (OrdinaryList a)

मुख्य अंतर यह है कि ListFएक अतिरिक्त प्रकार का पैरामीटर ( next) लिया जाता है, जिसे हम टाइप के सभी पुनरावर्ती / corecursive घटनाओं के लिए एक प्लेसहोल्डर के रूप में उपयोग करते हैं।

अब, ListFहम से लैस कर सकते हैं, इस तरह पुनरावर्ती और corecursive सूचियों को परिभाषित कर सकते हैं:

type List a = LFix (ListF a)

type CoList a = GFix (ListF a)

... कहाँ पे:

  • List पुनरावर्तन के लिए भाषा समर्थन के बिना एक पुनरावर्ती सूची कार्यान्वित की जाती है
  • CoList एक corecursive सूची है जो corecursion के लिए भाषा समर्थन के बिना लागू की गई है

ये दोनों प्रकार ("आइसोमॉर्फिक") के बराबर हैं [], जिसका अर्थ है:

  • आप reversibly आगे पीछे परिवर्तित कर सकते हैं के बीच Listऔर[]
  • आप reversibly आगे पीछे परिवर्तित कर सकते हैं के बीच CoListऔर[]

आइए साबित करते हैं कि उन रूपांतरण कार्यों को परिभाषित करके!

fromList :: List a -> [a]
fromList (LFix f) = f adapt
  where
    adapt (Cons a next) = a : next
    adapt  Nil          = []

toList :: [a] -> List a
toList xs = LFix (\k -> foldr (\a x -> k (Cons a x)) (k Nil) xs)

fromCoList :: CoList a -> [a]
fromCoList (GFix start step) = loop start
  where
    loop state = case step state of
        Nil           -> []
        Cons a state' -> a : loop state'

toCoList :: [a] -> CoList a
toCoList xs = GFix xs step
  where
    step      []  = Nil
    step (y : ys) = Cons y ys

तो Dhall प्रकार को लागू करने में पहला कदम पुनरावर्ती Graphप्रकार को बदलना था :

data Graph a = Node { id :: a, neighbors :: [ Graph a ] }

... बराबर सह-पुनरावर्ती प्रतिनिधित्व करने के लिए:

data GraphF a next = Node { id ::: a, neighbors :: [ next ] }

data GFix f = forall x . GFix x (x -> f x)

type Graph a = GFix (GraphF a)

... हालांकि थोड़ा सरल प्रकारों को आसान करने के लिए मुझे लगता है कि GFixइस मामले में विशेषज्ञ के लिए आसान है जहां f = GraphF:

data GraphF a next = Node { id ::: a, neighbors :: [ next ] }

data Graph a = forall x . Graph x (x -> GraphF a x)

हास्केल के पास धल्ल की तरह गुमनाम रिकॉर्ड नहीं है, लेकिन अगर ऐसा होता है तो हम परिभाषा को आगे बढ़ाकर इस प्रकार को सरल बना सकते हैं GraphF:

data Graph a = forall x . MakeGraph x (x -> { id :: a, neighbors :: [ x ] })

अब यह देखने के लिए Dhall प्रकार की तरह लग रहा है Graph, खासकर अगर हम इसके xसाथ प्रतिस्थापित करते हैं node:

data Graph a = forall node . MakeGraph node (node -> { id :: a, neighbors :: [ node ] })

हालांकि, अभी भी एक आखिरी मुश्किल हिस्सा है, जो ExistentialQuantificationहास्केल से ढल्ल तक अनुवाद करने का तरीका है । यह पता चलता है कि आप forallनिम्न समतुल्यता का उपयोग करके अस्तित्वीय परिमाणीकरण को सार्वभौमिक परिमाणीकरण (यानी ) में बदल सकते हैं :

exists y . f y ≅ forall x . (forall y . f y -> x) -> x

मेरा मानना ​​है कि इसे "स्कोलमाइजेशन" कहा जाता है

अधिक जानकारी के लिए, देखें:

... और वह अंतिम चाल आपको Dhall प्रकार देती है:

let Graph
    : Type
    =     forall (Graph : Type)
      ->  forall  ( MakeGraph
                  :     forall (Node : Type)
                    ->  Node
                    ->  (Node -> { id : Text, neighbors : List Node })
                    ->  Graph
                  )
      ->  Graph

... जहां forall (Graph : Type)के रूप में ही भूमिका निभाता है forall xपिछले सूत्र में और forall (Node : Type)के रूप में ही भूमिका निभाता है forall yपिछले सूत्र में।


1
इस उत्तर के लिए, और धल्ल को विकसित करने के लिए आवश्यक कड़ी मेहनत के लिए बहुत बहुत धन्यवाद! क्या आप Dhall / System F को किसी भी नई सामग्री का सुझाव दे सकते हैं, यह समझने के लिए कि आप यहाँ क्या कर रहे हैं, यह समझने के लिए कि अन्य संभावित ग्राफ निरूपण क्या हो सकते हैं? मैं एक फ़ंक्शन लिखने के लिए यहां जो कुछ भी किया है, उसका विस्तार करने में सक्षम होना चाहता हूं जो आपके ग्राफ़ प्रकार के किसी भी मूल्य से गहराई से पहली खोज के माध्यम से आसन्न सूची प्रतिनिधित्व का उत्पादन कर सकता है।
ब्योर्न वेस्टरगार्ड

4
@ BjørnWestergard: आपका स्वागत है! इसके पीछे के सिद्धांत को समझाने के लिए मैंने अपना जवाब संपादित किया, जिसमें उपयोगी संदर्भ भी शामिल हैं
गेब्रियल गोंजालेज
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.