हां, आप इस तरह से एक सुरक्षित, निर्देशित, संभवतः-चक्रीय, ग्राफ में मॉडल बना सकते हैं:
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
पिछले सूत्र में।