হ্যাঁ, আপনি কোনও ধরণের সুরক্ষিত, পরিচালিত, সম্ভবত-চক্রীয়, গ্রাফের ধালে মডেল করতে পারেন:
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 ] }
আপনি এই ধরণের একটি অলস এবং সম্ভাব্য অসীম ডেটা স্ট্রাকচার হিসাবে ভাবতে পারেন যে আপনি কেবল প্রতিবেশীদের দেখা করা চালিয়ে গেলে কী পাবেন represent
এখন, ভান করা যাক যে উপরোক্ত Tree
উপস্থাপনাটি আসলে আমাদের হ'লGraph
ডেটাটাইপটির নামকরণ করে Graph
:
data Graph a = Node { id :: a, neighbors :: [ Graph a ] }
... তবে আমরা যদি এই ধরণেরটি ব্যবহার করতে চাইতাম তবে আমাদের সরাসরি ধলে inাল টাইপের মডেল করার কোনও উপায় নেই কারণ ঝাল ভাষাটি পুনরাবৃত্ত ডেটা কাঠামোর জন্য অন্তর্নির্মিত সমর্থন সরবরাহ করে না। তাই আমরা কি কাজ করতে পারি?
ভাগ্যক্রমে, Dhaাল-এর মতো অ-পুনরাবৃত্ত ভাষায় রিকার্সিভ ডেটা স্ট্রাকচার এবং পুনরাবৃত্ত ফাংশনগুলি এম্বেড করার আসলে উপায় আছে। আসলে, দুটি উপায় আছে!
আমি প্রথম যে জিনিসটি এই কৌশলটির সাথে আমাকে পরিচয় করিয়েছিলাম তা হ'ল ওয়েডলারের নিম্নলিখিত খসড়া পোস্ট:
... তবে আমি নিম্নলিখিত দুটি হাস্কেল প্রকারটি ব্যবহার করে মূল ধারণাটি সংক্ষেপ করতে পারি:
{-# 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
), যা আমরা সমস্ত পুনরাবৃত্ত / কর্সারসিভ ঘটনার জন্য স্থানধারক হিসাবে ব্যবহার করি।
এখন, সজ্জিত ListF
, আমরা এরকম পুনরাবৃত্ত এবং কর্সারসিভ তালিকা সংজ্ঞায়িত করতে পারি:
type List a = LFix (ListF a)
type CoList a = GFix (ListF a)
... কোথায়:
List
পুনরাবৃত্তির জন্য ভাষা সমর্থন ছাড়াই কার্যকর করা একটি পুনরাবৃত্তির তালিকা
CoList
কোরকর্সনের জন্য ভাষা সমর্থন ছাড়াই কার্যকর একটি কর্সারসিভ তালিকা
এই উভয় প্রকারের সমতুল্য ("isomorphic to") []
যার অর্থ:
- আপনি 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
সুতরাং llাল ধরণের প্রয়োগের প্রথম পদক্ষেপটি ছিল পুনরাবৃত্তির 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 ] })
এটি এখন একটি ঝাল ধরণের মতো দেখতে শুরু করছে 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
আমি বিশ্বাস করি এটিকে "স্কোলাইমাইজেশন" বলা হয়
আরও তথ্যের জন্য, দেখুন:
... এবং সেই চূড়ান্ত কৌশলটি আপনাকে ধল ধরণের দেয়:
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
পূর্ববর্তী সূত্রে।