चिट्टी-चिट्टी-बैंग-बैंग में चाइल्डकैचर की तरह बच्चों को मिठाई और खिलौनों के साथ कैद में रखना, अंडर ग्रेजुएट फिजिक्स में भर्ती होना जैसे साबुन के बुलबुले और बुमेरांग के साथ मूर्ख बनाना, लेकिन जब दरवाजा बंद हो जाता है, तो यह "सही है, बच्चों, सीखने का समय आंशिक भेदभाव के बारे में! ”। मैं भी। यह मत कहो कि मैंने तुम्हें चेतावनी नहीं दी।
यहां एक और चेतावनी दी गई है: निम्नलिखित कोड की जरूरत है {-# LANGUAGE KitchenSink #-}
, या बल्कि
{-# LANGUAGE TypeFamilies, FlexibleContexts, TupleSections, GADTs, DataKinds,
TypeOperators, FlexibleInstances, RankNTypes, ScopedTypeVariables,
StandaloneDeriving, UndecidableInstances #-}
किसी विशेष क्रम में नहीं।
अलग-अलग फंक्शनलर्स कॉमोनैडिक जिपर देते हैं
वैसे भी, एक अलग फ़नकार क्या है?
class (Functor f, Functor (DF f)) => Diff1 f where
type DF f :: * -> *
upF :: ZF f x -> f x
downF :: f x -> f (ZF f x)
aroundF :: ZF f x -> ZF f (ZF f x)
data ZF f x = (:<-:) {cxF :: DF f x, elF :: x}
यह एक फ़ंक्टर है जिसमें एक व्युत्पन्न है, जो एक फ़नकार भी है। व्युत्पन्न एक तत्व के लिए एक छेद के संदर्भ का प्रतिनिधित्व करता है । ज़िप प्रकार ZF f x
एक छेद के संदर्भ और छेद में तत्व की जोड़ी का प्रतिनिधित्व करता है।
Diff1
नेविगेशन के प्रकारों का वर्णन करने के लिए ऑपरेशन हम ज़िपर पर कर सकते हैं (बिना किसी "वामपंथी" और "सही" की धारणा के बिना, जिसके लिए मेरे जोकर और जोकर देखें पेपर देखें)। हम "ऊपर की ओर" जा सकते हैं, इसके छेद में तत्व को प्लग करके संरचना को फिर से जोड़ सकते हैं। हम "नीचे की ओर" जा सकते हैं, किसी संरचना में एक तत्व का दौरा करने के लिए हर तरह की खोज कर सकते हैं: हम हर तत्व को उसके संदर्भ से सजाते हैं। हम "आसपास" जा सकते हैं, एक मौजूदा जिपर ले जा रहे हैं और प्रत्येक तत्व को उसके संदर्भ से सजा सकते हैं, इसलिए हम refocus के सभी तरीके ढूंढते हैं (और अपना वर्तमान ध्यान कैसे रखें)।
अब, प्रकार aroundF
आप में से कुछ की याद दिला सकता है
class Functor c => Comonad c where
extract :: c x -> x
duplicate :: c x -> c (c x)
और आपको याद दिलाना सही है! हमारे पास एक हॉप और एक स्किप के साथ है,
instance Diff1 f => Functor (ZF f) where
fmap f (df :<-: x) = fmap f df :<-: f x
instance Diff1 f => Comonad (ZF f) where
extract = elF
duplicate = aroundF
और हम जोर देते हैं कि
extract . duplicate == id
fmap extract . duplicate == id
duplicate . duplicate == fmap duplicate . duplicate
हमें भी यही चाहिए
fmap extract (downF xs) == xs
fmap upF (downF xs) = fmap (const xs) xs
बहुपद फंक्शनल भिन्न होते हैं
लगातार फंक्शनल भिन्न होते हैं।
data KF a x = KF a
instance Functor (KF a) where
fmap f (KF a) = KF a
instance Diff1 (KF a) where
type DF (KF a) = KF Void
upF (KF w :<-: _) = absurd w
downF (KF a) = KF a
aroundF (KF w :<-: _) = absurd w
एक तत्व रखने के लिए कहीं नहीं है, इसलिए एक संदर्भ बनाना असंभव है। वहाँ जाने के लिए upF
या कहीं से भी नहीं है downF
, और हम आसानी से जाने के सभी तरीकों में से कोई भी नहीं पाते हैं downF
।
पहचान functor विभेदक है।
data IF x = IF x
instance Functor IF where
fmap f (IF x) = IF (f x)
instance Diff1 IF where
type DF IF = KF ()
upF (KF () :<-: x) = IF x
downF (IF x) = IF (KF () :<-: x)
aroundF z@(KF () :<-: x) = KF () :<-: z
एक तुच्छ संदर्भ में एक तत्व है, downF
इसे पाता है, upF
इसे दोहराता है, और aroundF
केवल डाल ही रह सकता है।
सम विभिन्नता को संरक्षित करता है।
data (f :+: g) x = LF (f x) | RF (g x)
instance (Functor f, Functor g) => Functor (f :+: g) where
fmap h (LF f) = LF (fmap h f)
fmap h (RF g) = RF (fmap h g)
instance (Diff1 f, Diff1 g) => Diff1 (f :+: g) where
type DF (f :+: g) = DF f :+: DF g
upF (LF f' :<-: x) = LF (upF (f' :<-: x))
upF (RF g' :<-: x) = RF (upF (g' :<-: x))
अन्य बिट्स और टुकड़े एक मुट्ठी भर से थोड़ा अधिक हैं। जाने के लिए downF
, हमें downF
टैग किए गए घटक के अंदर जाना होगा , फिर संदर्भ में टैग दिखाने के लिए परिणामी ज़िपर को ठीक करना होगा।
downF (LF f) = LF (fmap (\ (f' :<-: x) -> LF f' :<-: x) (downF f))
downF (RF g) = RF (fmap (\ (g' :<-: x) -> RF g' :<-: x) (downF g))
जाने के लिए aroundF
, हम टैग को हटाते हैं, पता लगाते हैं कि कैसे किसी अनछुई चीज़ के चारों ओर जाना है, फिर सभी परिणामी ज़िपर में टैग को पुनर्स्थापित करें। ध्यान में तत्व, x
,, अपने पूरे ज़िपर ने ले ली है z
।
aroundF z@(LF f' :<-: (x :: x)) =
LF (fmap (\ (f' :<-: x) -> LF f' :<-: x) . cxF $ aroundF (f' :<-: x :: ZF f x))
:<-: z
aroundF z@(RF g' :<-: (x :: x)) =
RF (fmap (\ (g' :<-: x) -> RF g' :<-: x) . cxF $ aroundF (g' :<-: x :: ZF g x))
:<-: z
ध्यान दें कि मुझे ScopedTypeVariables
पुनरावर्ती कॉलों को अस्वीकार करने के लिए उपयोग करना था aroundF
। एक प्रकार के कार्य के रूप में, DF
इंजेक्शन नहीं है, इसलिए यह तथ्य जो f' :: D f x
बल देने के लिए पर्याप्त नहीं है f' :<-: x :: Z f x
।
उत्पाद भिन्नता को संरक्षित करता है।
data (f :*: g) x = f x :*: g x
instance (Functor f, Functor g) => Functor (f :*: g) where
fmap h (f :*: g) = fmap h f :*: fmap h g
एक जोड़ी में एक तत्व पर ध्यान केंद्रित करने के लिए, आप या तो बाईं ओर ध्यान केंद्रित करते हैं और सही अकेले छोड़ देते हैं, या इसके विपरीत। लिबनीज का प्रसिद्ध उत्पाद नियम एक साधारण स्थानिक अंतर्ज्ञान से मेल खाता है!
instance (Diff1 f, Diff1 g) => Diff1 (f :*: g) where
type DF (f :*: g) = (DF f :*: g) :+: (f :*: DF g)
upF (LF (f' :*: g) :<-: x) = upF (f' :<-: x) :*: g
upF (RF (f :*: g') :<-: x) = f :*: upF (g' :<-: x)
अब, downF
यह उसी तरह से काम करता है जिस तरह से रकम के लिए किया था, सिवाय इसके कि हमें जिपर संदर्भ को न केवल एक टैग के साथ ठीक करना होगा (यह दिखाने के लिए कि हम किस रास्ते पर गए थे) बल्कि अछूते अन्य घटक के साथ भी।
downF (f :*: g)
= fmap (\ (f' :<-: x) -> LF (f' :*: g) :<-: x) (downF f)
:*: fmap (\ (g' :<-: x) -> RF (f :*: g') :<-: x) (downF g)
लेकिन aroundF
हंसी का एक बड़ा बैग है। जिस भी पक्ष में हम वर्तमान में जा रहे हैं, हमारे पास दो विकल्प हैं:
aroundF
उस तरफ चलो ।
upF
उस तरफ से हटो और downF
दूसरी तरफ।
प्रत्येक मामले में हमें उपप्रकार के लिए संचालन का उपयोग करने की आवश्यकता होती है, फिर संदर्भों को ठीक करें।
aroundF z@(LF (f' :*: g) :<-: (x :: x)) =
LF (fmap (\ (f' :<-: x) -> LF (f' :*: g) :<-: x)
(cxF $ aroundF (f' :<-: x :: ZF f x))
:*: fmap (\ (g' :<-: x) -> RF (f :*: g') :<-: x) (downF g))
:<-: z
where f = upF (f' :<-: x)
aroundF z@(RF (f :*: g') :<-: (x :: x)) =
RF (fmap (\ (f' :<-: x) -> LF (f' :*: g) :<-: x) (downF f) :*:
fmap (\ (g' :<-: x) -> RF (f :*: g') :<-: x)
(cxF $ aroundF (g' :<-: x :: ZF g x)))
:<-: z
where g = upF (g' :<-: x)
ओह! बहुपद सभी भिन्न होते हैं, और इस तरह हमें कॉमनॉड्स देते हैं।
हम्म। यह थोड़ा सार है। इसलिए मैंने deriving Show
हर जगह जोड़ा जो मैं कर सकता था, और अंदर फेंक दिया
deriving instance (Show (DF f x), Show x) => Show (ZF f x)
जो निम्नलिखित बातचीत की अनुमति दी (हाथ से tidied)
> downF (IF 1 :*: IF 2)
IF (LF (KF () :*: IF 2) :<-: 1) :*: IF (RF (IF 1 :*: KF ()) :<-: 2)
> fmap aroundF it
IF (LF (KF () :*: IF (RF (IF 1 :*: KF ()) :<-: 2)) :<-: (LF (KF () :*: IF 2) :<-: 1))
:*:
IF (RF (IF (LF (KF () :*: IF 2) :<-: 1) :*: KF ()) :<-: (RF (IF 1 :*: KF ()) :<-: 2))
एक्सरसाइज से पता चलता है कि चेन नियम का उपयोग करते हुए डिफरेंशियल फंक्शनलर्स की रचना अलग-अलग होती है ।
मिठाई! क्या अब हम घर जा सकते हैं? बिलकूल नही। हमने अभी तक किसी भी पुनरावर्ती संरचनाओं को विभेदित नहीं किया है।
बिफंकर्स से पुनरावर्ती फंक्शंस बनाना
ए Bifunctor
, डेटाटाइप जेनेरिक प्रोग्रामिंग पर मौजूदा साहित्य के रूप में (पैट्रिक जानसन और जोहान ज्यूरिंग द्वारा काम, या जेरेमी गिबन्स द्वारा उत्कृष्ट व्याख्यान नोट्स) बताते हैं कि लंबाई दो प्रकार के साथ एक प्रकार का निर्माता है, जो दो प्रकार के उपप्रकार के अनुरूप है। हम दोनों को "मैप" करने में सक्षम होना चाहिए।
class Bifunctor b where
bimap :: (x -> x') -> (y -> y') -> b x y -> b x' y'
हम Bifunctor
पुनरावर्ती कंटेनरों की नोड संरचना देने के लिए एस का उपयोग कर सकते हैं । प्रत्येक नोड में सबनॉड्स और तत्व होते हैं । ये सिर्फ दो प्रकार के उपप्रकार हो सकते हैं।
data Mu b y = In (b (Mu b y) y)
देख? हम "पुनरावर्ती गाँठ" को b
पहले तर्क में बाँधते हैं, और y
इसके दूसरे भाग में पैरामीटर रखते हैं । तदनुसार, हम सभी के लिए एक बार प्राप्त करते हैं
instance Bifunctor b => Functor (Mu b) where
fmap f (In b) = In (bimap (fmap f) f b)
इसका उपयोग करने के लिए, हमें Bifunctor
उदाहरणों की एक किट की आवश्यकता होगी ।
बिफुनक्टर किट
लगातार द्विभाजक होते हैं।
newtype K a x y = K a
instance Bifunctor (K a) where
bimap f g (K a) = K a
आप बता सकते हैं कि मैंने यह पहले लिखा था, क्योंकि पहचानकर्ता कम हैं, लेकिन यह अच्छा है क्योंकि कोड लंबा है।
वेरिएबल्स बिफ्यूनेक्टरियल हैं।
हमें एक पैरामीटर या दूसरे के अनुरूप बिफुनक्टर्स की आवश्यकता है, इसलिए मैंने उन्हें अलग करने के लिए एक डेटाटाइप बनाया, फिर एक उपयुक्त जीएडीटी को परिभाषित किया।
data Var = X | Y
data V :: Var -> * -> * -> * where
XX :: x -> V X x y
YY :: y -> V Y x y
की V X x y
एक प्रति x
और की V Y x y
एक प्रति बनाता है y
। तदनुसार
instance Bifunctor (V v) where
bimap f g (XX x) = XX (f x)
bimap f g (YY y) = YY (g y)
रकम और उत्पाद bifunctors की bifunctors हैं
data (:++:) f g x y = L (f x y) | R (g x y) deriving Show
instance (Bifunctor b, Bifunctor c) => Bifunctor (b :++: c) where
bimap f g (L b) = L (bimap f g b)
bimap f g (R b) = R (bimap f g b)
data (:**:) f g x y = f x y :**: g x y deriving Show
instance (Bifunctor b, Bifunctor c) => Bifunctor (b :**: c) where
bimap f g (b :**: c) = bimap f g b :**: bimap f g c
अब तक, इतनी बॉयलरप्लेट, लेकिन अब हम जैसी चीजों को परिभाषित कर सकते हैं
List = Mu (K () :++: (V Y :**: V X))
Bin = Mu (V Y :**: (K () :++: (V X :**: V X)))
यदि आप वास्तविक डेटा के लिए इन प्रकारों का उपयोग करना चाहते हैं और जॉर्जेस सेराट की पॉइंटिलिस्ट परंपरा में अंधे नहीं हैं, तो पैटर्न समानार्थक शब्द का उपयोग करें ।
लेकिन क्या zippers? हम कैसे दिखाएंगे कि Mu b
यह अलग है? हमें यह दिखाने की आवश्यकता होगी कि दोनों चर b
में भिन्नता है । दबंग! यह आंशिक भेदभाव के बारे में जानने का समय है।
बिफुनक्टर्स का आंशिक डेरिवेटिव
क्योंकि हमारे पास दो चर हैं, हमें अन्य समय में सामूहिक रूप से और कभी-कभी उनके बारे में बात करने में सक्षम होना चाहिए। हमें एकल परिवार की आवश्यकता होगी:
data Vary :: Var -> * where
VX :: Vary X
VY :: Vary Y
अब हम यह कह सकते हैं कि बिफुनक्टर के प्रत्येक चर पर आंशिक व्युत्पन्न होने का क्या अर्थ है, और जिपर की इसी धारणा को दें।
class (Bifunctor b, Bifunctor (D b X), Bifunctor (D b Y)) => Diff2 b where
type D b (v :: Var) :: * -> * -> *
up :: Vary v -> Z b v x y -> b x y
down :: b x y -> b (Z b X x y) (Z b Y x y)
around :: Vary v -> Z b v x y -> Z b v (Z b X x y) (Z b Y x y)
data Z b v x y = (:<-) {cxZ :: D b v x y, elZ :: V v x y}
इस D
ऑपरेशन के लिए यह जानना आवश्यक है कि किस चर को लक्षित किया जाए। संबंधित ज़िप Z b v
हमें बताता है कि कौन सा चर v
फोकस में होना चाहिए। जब हम "संदर्भ के x
साथ X
सजते हैं ", तो हमें -contexts के साथ--ments और -contexts के y
साथ- Y
सजावट करना होगा। लेकिन अन्यथा, यह वही कहानी है।
हमारे पास दो शेष कार्य हैं: सबसे पहले, यह दर्शाने के लिए कि हमारा बाइफंक्टर किट अलग है; दूसरे, यह दिखाने के लिए कि Diff2 b
हमें स्थापित करने की अनुमति देता है Diff1 (Mu b)
।
Bifunctor किट में अंतर करना
मुझे डर है कि एडिट करने के बजाय यह बिट फिडली है। बेझिझक साथ छोड़ें।
स्थिरांक पहले की तरह हैं।
instance Diff2 (K a) where
type D (K a) v = K Void
up _ (K q :<- _) = absurd q
down (K a) = K a
around _ (K q :<- _) = absurd q
इस अवसर पर, प्रकार स्तर क्रोनकर-डेल्टा के सिद्धांत को विकसित करने के लिए जीवन बहुत छोटा है, इसलिए मैंने बस चर अलग से व्यवहार किया।
instance Diff2 (V X) where
type D (V X) X = K ()
type D (V X) Y = K Void
up VX (K () :<- XX x) = XX x
up VY (K q :<- _) = absurd q
down (XX x) = XX (K () :<- XX x)
around VX z@(K () :<- XX x) = K () :<- XX z
around VY (K q :<- _) = absurd q
instance Diff2 (V Y) where
type D (V Y) X = K Void
type D (V Y) Y = K ()
up VX (K q :<- _) = absurd q
up VY (K () :<- YY y) = YY y
down (YY y) = YY (K () :<- YY y)
around VX (K q :<- _) = absurd q
around VY z@(K () :<- YY y) = K () :<- YY z
संरचनात्मक मामलों के लिए, मैंने एक सहायक को शुरू करने के लिए उपयोगी पाया जिससे मुझे समान रूप से चर का इलाज करने की अनुमति मिली।
vV :: Vary v -> Z b v x y -> V v (Z b X x y) (Z b Y x y)
vV VX z = XX z
vV VY z = YY z
मैं तो "retagging" हम के लिए की जरूरत है की तरह की सुविधा के लिए गैजेट बनाया down
और around
। (बेशक, मैंने देखा कि मुझे काम करने के दौरान कौन से गैजेट्स की ज़रूरत थी।)
zimap :: (Bifunctor c) => (forall v. Vary v -> D b v x y -> D b' v x y) ->
c (Z b X x y) (Z b Y x y) -> c (Z b' X x y) (Z b' Y x y)
zimap f = bimap
(\ (d :<- XX x) -> f VX d :<- XX x)
(\ (d :<- YY y) -> f VY d :<- YY y)
dzimap :: (Bifunctor (D c X), Bifunctor (D c Y)) =>
(forall v. Vary v -> D b v x y -> D b' v x y) ->
Vary v -> Z c v (Z b X x y) (Z b Y x y) -> D c v (Z b' X x y) (Z b' Y x y)
dzimap f VX (d :<- _) = bimap
(\ (d :<- XX x) -> f VX d :<- XX x)
(\ (d :<- YY y) -> f VY d :<- YY y)
d
dzimap f VY (d :<- _) = bimap
(\ (d :<- XX x) -> f VX d :<- XX x)
(\ (d :<- YY y) -> f VY d :<- YY y)
d
और जाने के लिए तैयार होने के साथ, हम विवरणों को बाहर निकाल सकते हैं। रकम आसान है।
instance (Diff2 b, Diff2 c) => Diff2 (b :++: c) where
type D (b :++: c) v = D b v :++: D c v
up v (L b' :<- vv) = L (up v (b' :<- vv))
down (L b) = L (zimap (const L) (down b))
down (R c) = R (zimap (const R) (down c))
around v z@(L b' :<- vv :: Z (b :++: c) v x y)
= L (dzimap (const L) v ba) :<- vV v z
where ba = around v (b' :<- vv :: Z b v x y)
around v z@(R c' :<- vv :: Z (b :++: c) v x y)
= R (dzimap (const R) v ca) :<- vV v z
where ca = around v (c' :<- vv :: Z c v x y)
उत्पाद कड़ी मेहनत करते हैं, यही वजह है कि मैं एक इंजीनियर के बजाय एक गणितज्ञ हूं।
instance (Diff2 b, Diff2 c) => Diff2 (b :**: c) where
type D (b :**: c) v = (D b v :**: c) :++: (b :**: D c v)
up v (L (b' :**: c) :<- vv) = up v (b' :<- vv) :**: c
up v (R (b :**: c') :<- vv) = b :**: up v (c' :<- vv)
down (b :**: c) =
zimap (const (L . (:**: c))) (down b) :**: zimap (const (R . (b :**:))) (down c)
around v z@(L (b' :**: c) :<- vv :: Z (b :**: c) v x y)
= L (dzimap (const (L . (:**: c))) v ba :**:
zimap (const (R . (b :**:))) (down c))
:<- vV v z where
b = up v (b' :<- vv :: Z b v x y)
ba = around v (b' :<- vv :: Z b v x y)
around v z@(R (b :**: c') :<- vv :: Z (b :**: c) v x y)
= R (zimap (const (L . (:**: c))) (down b):**:
dzimap (const (R . (b :**:))) v ca)
:<- vV v z where
c = up v (c' :<- vv :: Z c v x y)
ca = around v (c' :<- vv :: Z c v x y)
वैचारिक रूप से, यह पहले की तरह ही है, लेकिन अधिक नौकरशाही के साथ। मैंने इन्हें प्री-टाइप-होल तकनीक का उपयोग करते हुए बनाया था, उन undefined
जगहों पर ठूंठ के रूप में इस्तेमाल किया जाता था जो मैं काम करने के लिए तैयार नहीं था, और एक जगह (किसी भी समय) में एक जानबूझकर प्रकार की त्रुटि का परिचय देते हुए जहां मैं टाइपसेकर से एक उपयोगी संकेत चाहता था। । आप हास्केल में भी, वीडियोगेम अनुभव के रूप में टाइपकास्ट कर सकते हैं।
पुनरावर्ती कंटेनरों के लिए सबनोड ज़िपर
b
सम्मान के साथ आंशिक व्युत्पन्न X
हमें बताता है कि एक नोड के अंदर एक कदम एक सबनोड कैसे खोजना है, इसलिए हमें जिपर की पारंपरिक धारणा मिलती है।
data MuZpr b y = MuZpr
{ aboveMu :: [D b X (Mu b y) y]
, hereMu :: Mu b y
}
हम X
पदों में बार-बार प्लगिंग करके रूट तक सभी को ज़ूम कर सकते हैं ।
muUp :: Diff2 b => MuZpr b y -> Mu b y
muUp (MuZpr {aboveMu = [], hereMu = t}) = t
muUp (MuZpr {aboveMu = (dX : dXs), hereMu = t}) =
muUp (MuZpr {aboveMu = dXs, hereMu = In (up VX (dX :<- XX t))})
लेकिन हमें एलिमेंट -zippers चाहिए।
Bifunctors के निर्धारण के लिए तत्व- zippers
प्रत्येक तत्व एक नोड के अंदर कहीं है। वह नोड- X
थरथराहट के ढेर के नीचे बैठा है । लेकिन उस नोड में तत्व की स्थिति एक Y
एडरिवेटिव द्वारा दी गई है । हमें मिला
data MuCx b y = MuCx
{ aboveY :: [D b X (Mu b y) y]
, belowY :: D b Y (Mu b y) y
}
instance Diff2 b => Functor (MuCx b) where
fmap f (MuCx { aboveY = dXs, belowY = dY }) = MuCx
{ aboveY = map (bimap (fmap f) f) dXs
, belowY = bimap (fmap f) f dY
}
साहसपूर्वक, मैं दावा करता हूं
instance Diff2 b => Diff1 (Mu b) where
type DF (Mu b) = MuCx b
लेकिन इससे पहले कि मैं ऑपरेशन विकसित करूं, मुझे कुछ बिट्स और टुकड़ों की आवश्यकता होगी।
मैं फनकार-जिपर्स और बिफुनक्टर-जिपर्स के बीच डेटा का व्यापार कर सकता हूं:
zAboveY :: ZF (Mu b) y -> [D b X (Mu b y) y]
zAboveY (d :<-: y) = aboveY d
zZipY :: ZF (Mu b) y -> Z b Y (Mu b y) y
zZipY (d :<-: y) = belowY d :<- YY y
मुझे परिभाषित करने के लिए पर्याप्त है:
upF z = muUp (MuZpr {aboveMu = zAboveY z, hereMu = In (up VY (zZipY z))})
यही है, हम पहले उस नोड को फिर से इकट्ठा करके चलते हैं जहां तत्व है, एक तत्व-ज़िप को एक सबनोड-ज़िप में बदलकर, फिर ऊपर के रूप में सभी तरह से ज़ूमिंग करना।
आगे, मैं कहता हूं
downF = yOnDown []
खाली स्टैक के साथ शुरू करने के लिए नीचे जाएं, और सहायक फ़ंक्शन को परिभाषित करें जो down
किसी भी स्टैक के नीचे से बार-बार जाता है :
yOnDown :: Diff2 b => [D b X (Mu b y) y] -> Mu b y -> Mu b (ZF (Mu b) y)
yOnDown dXs (In b) = In (contextualize dXs (down b))
अब, down b
केवल हमें नोड के अंदर ले जाता है। हमें जिन ज़िप की आवश्यकता है, उन्हें नोड के संदर्भ में भी ले जाना चाहिए। यही contextualise
है:
contextualize :: (Bifunctor c, Diff2 b) =>
[D b X (Mu b y) y] ->
c (Z b X (Mu b y) y) (Z b Y (Mu b y) y) ->
c (Mu b (ZF (Mu b) y)) (ZF (Mu b) y)
contextualize dXs = bimap
(\ (dX :<- XX t) -> yOnDown (dX : dXs) t)
(\ (dY :<- YY y) -> MuCx {aboveY = dXs, belowY = dY} :<-: y)
प्रत्येक Y
प्रस्ताव के लिए, हमें एक तत्व-जिपर देना चाहिए, इसलिए यह अच्छा है कि हम पूरे संदर्भ dXs
को रूट पर वापस जानते हैं , साथ ही साथ यह dY
भी बताता है कि तत्व अपने नोड में कैसे बैठता है। हर X
-पोजिशन के लिए, तलाशने के लिए एक और सबट्री होती है, इसलिए हम स्टैक बढ़ते हैं और चलते रहते हैं!
यह केवल ध्यान केंद्रित करने के व्यवसाय को छोड़ देता है। हम जहाँ भी रहे हैं, वहाँ से नीचे जा सकते हैं, या ऊपर जा सकते हैं, या ऊपर जा सकते हैं, या ऊपर जा सकते हैं और फिर किसी और रास्ते से नीचे जा सकते हैं। यहाँ जाता हैं।
aroundF z@(MuCx {aboveY = dXs, belowY = dY} :<-: _) = MuCx
{ aboveY = yOnUp dXs (In (up VY (zZipY z)))
, belowY = contextualize dXs (cxZ $ around VY (zZipY z))
} :<-: z
हमेशा की तरह, मौजूदा तत्व को इसके पूरे ज़िप से बदल दिया गया है। के लिएbelowY
हिस्सा है, हम देखो और कहाँ हम मौजूदा नोड में जा सकते हैं: हम या तो वैकल्पिक तत्व मिलेगा Y
-positions या आगे X
-subnodes पता लगाने के लिए है, तो हम contextualise
उन्हें। इस aboveY
भाग के लिए, हम जिस रास्ते X
पर जा रहे थे, उस पर फिर से भरोसा करने के बाद , हम अपने-अपने तरीके से काम कर सकते हैं।
yOnUp :: Diff2 b => [D b X (Mu b y) y] -> Mu b y ->
[D b X (Mu b (ZF (Mu b) y)) (ZF (Mu b) y)]
yOnUp [] t = []
yOnUp (dX : dXs) (t :: Mu b y)
= contextualize dXs (cxZ $ around VX (dX :<- XX t))
: yOnUp dXs (In (up VX (dX :<- XX t)))
रास्ते के प्रत्येक चरण पर, हम या तो कहीं और मुड़ सकते हैं around
, या ऊपर जा सकते हैं।
और बस! मैंने कानूनों का औपचारिक प्रमाण नहीं दिया है, लेकिन यह मुझे ऐसा लगता है जैसे ऑपरेशन सावधानीपूर्वक संदर्भ को सही ढंग से बनाए रखते हैं क्योंकि वे संरचना को क्रॉल करते हैं।
हमने क्या सीखा है?
भिन्नता चीज़-के-संदर्भ की धारणाओं को प्रेरित करती है, एक कोमोनाडिक संरचना को प्रेरित करती है जहां extract
आपको चीज़ मिलती है और duplicate
संदर्भ को अन्य चीजों की तलाश में संदर्भ की खोज करती है। यदि हमारे पास नोड्स के लिए उपयुक्त अंतर संरचना है, तो हम पूरे पेड़ों के लिए अंतर संरचना विकसित कर सकते हैं।
ओह, और अलग-अलग प्रकार के कंस्ट्रक्टर के प्रत्येक व्यक्ति को अलग-अलग तरीके से इलाज करना भयावह रूप से भयावह है। बेहतर तरीका यह है कि अनुक्रमित सेट के बीच फंक्शनलर्स के साथ काम किया जाए
f :: (i -> *) -> (o -> *)
जहां हम विभिन्न प्रकार के तत्वों को o
संग्रहीत करते हुए i
विभिन्न प्रकार की संरचना बनाते हैं । ये जैकबियन निर्माण के तहत बंद हैं
J f :: (i -> *) -> ((o, i) -> *)
जहाँ परिणामी- (o, i)
अवरोधों में से प्रत्येक एक आंशिक व्युत्पन्न है, आपको बता रहा है कि एक i
-बाधा में एक-छेद कैसे बनाया जाए o
। लेकिन यह एक और समय के लिए भरोसेमंद रूप से टाइप किया गया मजाक है।