जिपर कोमोनॉड्स, सामान्य रूप से


80

किसी भी कंटेनर प्रकार को देखते हुए हम (तत्व-केंद्रित) जिपर बना सकते हैं और जान सकते हैं कि यह संरचना एक कोमोनड है। यह हाल ही में निम्नलिखित प्रकार के लिए एक और ढेर अतिप्रवाह सवाल में अद्भुत विस्तार से पता लगाया गया था :

data Bin a = Branch (Bin a) a (Bin a) | Leaf a deriving Functor

निम्नलिखित ज़िप के साथ

data Dir = L | R
data Step a = Step a Dir (Bin a)   deriving Functor
data Zip  a = Zip [Step a] (Bin a) deriving Functor
instance Comonad Zip where ...

यह मामला है कि Zipएक Comonadहालांकि इसके उदाहरण के निर्माण के एक छोटे बालों वाले है। उस ने कहा, Zipपूरी तरह से यंत्रवत् से प्राप्त किया जा सकता हैTree और (मेरा मानना ​​है) किसी भी प्रकार से व्युत्पन्न इस तरह से स्वचालित रूप से एक है Comonad, इसलिए मुझे लगता है कि ऐसा होना चाहिए कि हम इन प्रकारों और उनके कोमोनॉडों को उदारतापूर्वक और स्वचालित रूप से बना सकें।

जिपर निर्माण के लिए सामान्यता प्राप्त करने की एक विधि निम्न वर्ग और प्रकार के परिवार का उपयोग है

data Zipper t a = Zipper { diff :: D t a, here :: a }

deriving instance Diff t => Functor (Zipper t)

class (Functor t, Functor (D t)) => Diff t where
  data D t :: * -> *
  inTo  :: t a -> t (Zipper t a)
  outOf :: Zipper t a -> t a

जिसे (कमोबेश) हास्केल कैफे थ्रेड्स में और कॉनल इलियट के ब्लॉग पर दिखाया गया है। इस वर्ग को विभिन्न मूल बीजीय प्रकारों के लिए त्वरित किया जा सकता है और इस प्रकार ADTs के डेरिवेटिव के बारे में बात करने के लिए एक सामान्य ढांचा प्रदान करता है।

तो, आखिरकार, मेरा सवाल यह है कि हम लिख सकते हैं या नहीं

instance Diff t => Comonad (Zipper t) where ...

जिसका उपयोग ऊपर वर्णित विशिष्ट कोमोनॉड उदाहरण को ग्रहण करने के लिए किया जा सकता है:

instance Diff Bin where
  data D Bin a = DBin { context :: [Step a], descend :: Maybe (Bin a, Bin a) }
  ...

दुर्भाग्य से, मेरे पास इस तरह के उदाहरण लिखने का कोई सौभाग्य नहीं है। है inTo/ outOfहस्ताक्षर पर्याप्त? क्या कुछ और प्रकारों को विवश करने की आवश्यकता है? क्या यह उदाहरण भी संभव है?


29
हमें एक मिनट दें ...
pigworker

आप के कार्यान्वयन के लिए एक संदर्भ है Diffके लिए Eitherऔर (,)? मेरे पास एक सरल सरल संभव समाधान है जिसे मैं जांचना चाहता हूं।
Cirdec

@Cirdec आप जरूरी नहीं कि इसे या तो लागू करना चाहते हैं, बल्कि इसके लिए Either1 f g x = Inl (f x) | Inr (g x)कॉनल के ब्लॉग का पूरा विवरण है।
जे। अब्राह्मसन

वास्तव में, Eitherइस ढांचे में काफी लागू नहीं किया जा सकता है (और उम्मीद है कि इस सवाल का एक सही जवाब इस मुद्दे को संबोधित करेगा) Zipperमान लें कि आप कम से कम एक लक्ष्य मूल्य को इंगित कर सकते हैं । बयाना में, यह उन प्रकारों के लिए संभव नहीं है जो "खाली" हो सकते हैं।
जे। अब्राहमसन

3
@ पैट्रिक यह सवाल वास्तव में सटीक है, हालांकि यह काफी उन्नत हास्केल सुविधाओं पर आधारित है। और Cirdec का आखिरी जवाब इतना लंबा नहीं है। उस सूअर के बच्चे की आदत होती है कि वह अपने जवाब बहुत ही अलग तरीके से देता है, जिसे ज्यादातर लोग सराहते हैं ।
अर्जन जोहान्सन

जवाबों:


113

चिट्टी-चिट्टी-बैंग-बैंग में चाइल्डकैचर की तरह बच्चों को मिठाई और खिलौनों के साथ कैद में रखना, अंडर ग्रेजुएट फिजिक्स में भर्ती होना जैसे साबुन के बुलबुले और बुमेरांग के साथ मूर्ख बनाना, लेकिन जब दरवाजा बंद हो जाता है, तो यह "सही है, बच्चों, सीखने का समय आंशिक भेदभाव के बारे में! ”। मैं भी। यह मत कहो कि मैंने तुम्हें चेतावनी नहीं दी।

यहां एक और चेतावनी दी गई है: निम्नलिखित कोड की जरूरत है {-# 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              -- downF decorates the element in position
fmap upF (downF xs) = fmap (const xs) xs   -- downF gives the correct context

बहुपद फंक्शनल भिन्न होते हैं

लगातार फंक्शनल भिन्न होते हैं।

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हंसी का एक बड़ा बैग है। जिस भी पक्ष में हम वर्तमान में जा रहे हैं, हमारे पास दो विकल्प हैं:

  1. aroundFउस तरफ चलो ।
  2. 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]  -- the stack of `X`-derivatives above me
zAboveY (d :<-: y) = aboveY d

zZipY :: ZF (Mu b) y -> Z b Y (Mu b y) y      -- the `Y`-zipper where I am
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। लेकिन यह एक और समय के लिए भरोसेमंद रूप से टाइप किया गया मजाक है।


2
"टाइप-चेकिंग विडियोगेम" के रूप में, या बल्कि, केवल प्रकारों के बारे में तर्क का उपयोग करते हुए, मैंने Comonadस्तर को पा लिया, लेकिन केवल एक वैकल्पिक अंत तक ही पहुंच सका। खेल खेलते हुए, मैं एक दिलचस्प और मुश्किल स्तर में भाग गया। टाइपराइचर ने कहा कि छेद का प्रकार a -> a(कुछ बड़े लंबे प्रकार के लिए a) था, लेकिन छेद को भरने से idकाम नहीं हुआ। समस्या यह थी a ~ D t ~ D r, और मुझे वास्तव में एक फ़ंक्शन की D r -> D tआवश्यकता थी, और इसके प्रमाण के साथ टाइपराइटर प्रदान करने की आवश्यकता थी D r ~ D t
सेर्डेक

3
इसलिए scopedTypeVariables का सावधानीपूर्वक उपयोग, उन क्षणों के लिए जब ghc कहता है (SPJ की आवाज़ में) "नहीं नहीं नहीं, मैं अनुमान लगाना चाहता हूँ !" लेकिन अनुमान लगाना बहुत कठिन है।
सुअर का बच्चा

12
संक्षिप्त उत्तर यह प्रतीत होता है कि डिफ को aroundअपने हस्ताक्षर में भी आवश्यकता है। लंबे उत्तर, ठीक है, हमेशा की तरह काल्पनिक रूप से आंख खोल रहा है। यह लिखने के लिए एक मिनट खर्च करने के लिए बहुत बहुत धन्यवाद!
जे। अब्राहमसन

1
टुकड़े कि में जाने downऔर aroundएक ही हैं। ऐसा लगता है कि हम दोनों को उन उत्पादों के लिए निर्दिष्ट करने में सक्षम होना चाहिए जैसे कि उन उत्पादों की तरह descend f (a :*: b) = pure (:*:) <*> f (InL . (:*: b)) a <*> f (InR . (a :*:)) bजहां descendकी तर्ज पर एक प्रकार है Applicative (m t) => (forall f g. (Diff f, Diff g) => (D f a -> D g a) -> f a -> m g (f a)) -> t a -> m t (t a)
Cirdec

1
aroundपूरी तरह से down, upऔर दूसरी व्युत्पन्न के संदर्भ में लिखा जा सकता है , कोड को फिर से उपयोग किए बिना upऔर downएक अतिरिक्त अमूर्त की आवश्यकता के बिना Applicativeइसे कैप्चर करना।
Cirdec

12

Comonadज़िपर के लिए उदाहरण है नहीं

instance (Diff t, Diff (D t)) => Comonad (Zipper t) where
    extract = here
    duplicate = fmap outOf . inTo

कहाँ outOfऔर खुद के लिए उदाहरण inToसे आते हैं । उपरोक्त उदाहरण कानून का उल्लंघन करता है । इसके बजाय यह व्यवहार करता है:DiffZipper tComonadfmap extract . duplicate == id

fmap extract . duplicate == \z -> fmap (const (here z)) z

डिफ (जिपर टी)

Diffके लिए उदाहरण Zipperउन्हें उत्पादों के रूप में पहचान करने और उत्पादों (नीचे) के लिए कोड पुन: उपयोग द्वारा प्रदान की जाती है।

-- Zippers are themselves products
toZipper :: (D t :*: Identity) a -> Zipper t a
toZipper (d :*: (Identity h)) = Zipper d h

fromZipper :: Zipper t a -> (D t :*: Identity) a
fromZipper (Zipper d h) = (d :*: (Identity h))

डेटा प्रकारों के बीच एक समरूपतावाद, और उनके डेरिवेटिव के बीच एक समरूपता को देखते हुए, हम एक प्रकार का inToऔर outOfदूसरे के लिए पुन: उपयोग कर सकते हैं ।

inToFor' :: (Diff r) =>
            (forall a.   r a ->   t a) ->
            (forall a.   t a ->   r a) ->
            (forall a. D r a -> D t a) ->
            (forall a. D t a -> D r a) ->
            t a -> t (Zipper t a)
inToFor' to from toD fromD = to . fmap (onDiff toD) . inTo . from

outOfFor' :: (Diff r) =>
            (forall a.   r a ->   t a) ->
            (forall a.   t a ->   r a) ->
            (forall a. D r a -> D t a) ->
            (forall a. D t a -> D r a) ->
            Zipper t a -> t a
outOfFor' to from toD fromD = to . outOf . onDiff fromD

उन प्रकारों के लिए जो मौजूदा Diffउदाहरण के लिए सिर्फ नए टाइप हैं, उनका डेरिवेटिव एक ही प्रकार का है। यदि हम उस प्रकार की समानता के बारे में टाइप चेकर को बताते हैं D r ~ D t, तो हम डेरिवेटिव के लिए एक आइसोमॉर्फिज्म प्रदान करने के बजाय इसका लाभ उठा सकते हैं।

inToFor :: (Diff r, D r ~ D t) =>
           (forall a. r a -> t a) ->
           (forall a. t a -> r a) ->
           t a -> t (Zipper t a)
inToFor to from = inToFor' to from id id

outOfFor :: (Diff r, D r ~ D t) =>
            (forall a. r a -> t a) ->
            (forall a. t a -> r a) ->
            Zipper t a -> t a
outOfFor to from = outOfFor' to from id id

इन उपकरणों से लैस, हम Diffउत्पादों को लागू करने के लिए उदाहरण का पुन: उपयोग कर सकते हैंDiff (Zipper t)

-- This requires undecidable instances, due to the need to take D (D t)
instance (Diff t, Diff (D t)) => Diff (Zipper t) where
    type D (Zipper t) = D ((D t) :*: Identity)
    -- inTo :: t        a -> t        (Zipper  t         a)
    -- inTo :: Zipper t a -> Zipper t (Zipper (Zipper t) a)
    inTo = inToFor toZipper fromZipper
    -- outOf :: Zipper  t         a -> t        a
    -- outOf :: Zipper (Zipper t) a -> Zipper t a
    outOf = outOfFor toZipper fromZipper

बॉयलरप्लेट

वास्तव में यहां प्रस्तुत कोड का उपयोग करने के लिए, हमें कुछ भाषा एक्सटेंशन, आयात और प्रस्तावित समस्या का समाधान चाहिए।

{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE RankNTypes #-}

import Control.Monad.Identity
import Data.Proxy
import Control.Comonad

data Zipper t a = Zipper { diff :: D t a, here :: a }

onDiff :: (D t a -> D u a) -> Zipper t a -> Zipper u a
onDiff f (Zipper d a) = Zipper (f d) a

deriving instance Diff t => Functor (Zipper t)
deriving instance (Eq (D t a), Eq a) => Eq (Zipper t a)
deriving instance (Show (D t a), Show a) => Show (Zipper t a)

class (Functor t, Functor (D t)) => Diff t where
  type D t :: * -> *
  inTo  :: t a -> t (Zipper t a)
  outOf :: Zipper t a -> t a

उत्पाद, रकम और निरंतरता

Diff (Zipper t)उदाहरण के कार्यान्वयन पर निर्भर करता है Diffउत्पादों के लिए :*:, रकम :+:, स्थिरांक Identity, और शून्य Proxy

data (:+:) a b x = InL (a x) | InR (b x)
    deriving (Eq, Show)
data (:*:) a b x = a x :*: b x
    deriving (Eq, Show)

infixl 7 :*:
infixl 6 :+:

deriving instance (Functor a, Functor b) => Functor (a :*: b)

instance (Functor a, Functor b) => Functor (a :+: b) where
    fmap f (InL a) = InL . fmap f $ a
    fmap f (InR b) = InR . fmap f $ b


instance (Diff a, Diff b) => Diff (a :*: b) where
    type D (a :*: b) = D a :*: b :+: a :*: D b
    inTo (a :*: b) = 
        (fmap (onDiff (InL . (:*: b))) . inTo) a :*:
        (fmap (onDiff (InR . (a :*:))) . inTo) b
    outOf (Zipper (InL (a :*: b)) x) = (:*: b) . outOf . Zipper a $ x
    outOf (Zipper (InR (a :*: b)) x) = (a :*:) . outOf . Zipper b $ x

instance (Diff a, Diff b) => Diff (a :+: b) where
    type D (a :+: b) = D a :+: D b
    inTo (InL a) = InL . fmap (onDiff InL) . inTo $ a
    inTo (InR b) = InR . fmap (onDiff InR) . inTo $ b
    outOf (Zipper (InL a) x) = InL . outOf . Zipper a $ x
    outOf (Zipper (InR a) x) = InR . outOf . Zipper a $ x

instance Diff (Identity) where
    type D (Identity) = Proxy
    inTo = Identity . (Zipper Proxy) . runIdentity
    outOf = Identity . here

instance Diff (Proxy) where
    type D (Proxy) = Proxy
    inTo = const Proxy
    outOf = const Proxy

बिन उदाहरण

मैंने Binउत्पादों की राशि के लिए एक समरूपता के रूप में उदाहरण दिया। हमें न केवल इसके व्युत्पन्न बल्कि इसके दूसरे व्युत्पन्न की भी आवश्यकता है

newtype Bin   a = Bin   {unBin   ::      (Bin :*: Identity :*: Bin :+: Identity)  a}
    deriving (Functor, Eq, Show)
newtype DBin  a = DBin  {unDBin  ::    D (Bin :*: Identity :*: Bin :+: Identity)  a}
    deriving (Functor, Eq, Show)
newtype DDBin a = DDBin {unDDBin :: D (D (Bin :*: Identity :*: Bin :+: Identity)) a}
    deriving (Functor, Eq, Show)

instance Diff Bin where
    type D Bin = DBin
    inTo  = inToFor'  Bin unBin DBin unDBin
    outOf = outOfFor' Bin unBin DBin unDBin

instance Diff DBin where
    type D DBin = DDBin
    inTo  = inToFor'  DBin unDBin DDBin unDDBin
    outOf = outOfFor' DBin unDBin DDBin unDDBin

पिछले उत्तर का उदाहरण डेटा है

aTree :: Bin Int    
aTree =
    (Bin . InL) (
        (Bin . InL) (
            (Bin . InR) (Identity 2)
            :*: (Identity 1) :*:
            (Bin . InR) (Identity 3)
        )
        :*: (Identity 0) :*:
        (Bin . InR) (Identity 4)
    )

कोमोनॉड उदाहरण नहीं

Binउपरोक्त उदाहरण के लिए एक काउंटर उदाहरण प्रदान करता है fmap outOf . inToका सही कार्यान्वयन किया जा रहा है duplicateके लिए Zipper t। विशेष रूप से, यह fmap extract . duplicate = idकानून को एक काउंटर-उदाहरण प्रदान करता है :

fmap ( \z -> (fmap extract . duplicate) z == z) . inTo $ aTree

जो मूल्यांकन करता है (ध्यान दें कि यह Falseहर जगह कैसे भरा है, कोई भी Falseकानून लागू करने के लिए पर्याप्त होगा)

Bin {unBin = InL ((Bin {unBin = InL ((Bin {unBin = InR (Identity False)} :*: Identity False) :*: Bin {unBin = InR (Identity False)})} :*: Identity False) :*: Bin {unBin = InR (Identity False)})}

inTo aTreeएक पेड़ है जिसकी संरचना एक समान है aTree, लेकिन हर जगह एक मूल्य था बजाय मूल्य के साथ एक ज़िप है, और शेष सभी मूल मूल्यों के साथ पेड़ शेष है। fmap (fmap extract . duplicate) . inTo $ aTreeउसी संरचना वाला एक पेड़ भी है aTree, लेकिन हर एक मूल्य था कि मूल्य के साथ एक जिपर है, और उस मूल्य के साथ प्रतिस्थापित सभी मूल्यों के साथ शेष पेड़ । दूसरे शब्दों में:

fmap extract . duplicate == \z -> fmap (const (here z)) z

सभी तीन के लिए पूर्ण परीक्षण सूट Comonadकानून, extract . duplicate == id, fmap extract . duplicate == id, और duplicate . duplicate == fmap duplicate . duplicateहै

main = do
    putStrLn "fmap (\\z -> (extract . duplicate) z == z) . inTo $ aTree"
    print   . fmap ( \z -> (extract . duplicate) z == z) . inTo $ aTree    
    putStrLn ""
    putStrLn  "fmap (\\z -> (fmap extract . duplicate) z == z) . inTo $ aTree"
    print    . fmap ( \z -> (fmap extract . duplicate) z == z) . inTo $ aTree    
    putStrLn ""
    putStrLn "fmap (\\z -> (duplicate . duplicate) z) == (fmap duplicate . duplicate) z) . inTo $ aTree"
    print   . fmap ( \z -> (duplicate . duplicate) z == (fmap duplicate . duplicate) z) . inTo $ aTree

1
upऔर downConal के ब्लॉग से जैसे ही हैं intoऔर outof
जे। अब्राहमसन

मैं देख सकता हूं कि @pigworker ने उसी रास्ते से नीचे जाने की कोशिश की, जो मैं एक साल पहले नीचे जाने की कोशिश कर रहा हूं। stackoverflow.com/questions/14133121/…
सेर्डेक

8

असीम रूप से भिन्न Diffश्रेणी को देखते हुए :

class (Functor t, Functor (D t)) => Diff t where
    type D t :: * -> *
    up :: Zipper t a -> t a
    down :: t a -> t (Zipper t a)  
    -- Require that types be infinitely differentiable
    ddiff :: p t -> Dict (Diff (D t))

around के संदर्भ में लिखा जा सकता है up और downपर Zipperके diffके derivitive, अनिवार्य रूप

around z@(Zipper d h) = Zipper ctx z
    where
        ctx = fmap (\z' -> Zipper (up z') (here z')) (down d)

Zipper t aएक के होते हैं D t aऔर एक a। हमें जाना , एक हो रही हर छेद में एक ज़िपर साथ। उन ज़िपर्स में एक होता है और वह छेद में होता है। हम उनमें से प्रत्येक के पास जाते हैं, एक हो रहे हैं और इसे साथ पार कर रहे हैंdownD t aD t (Zipper (D t) a)D (D t) aaupD t aa । A D t aऔर aa a बनाते हैं Zipper t a, जिससे हमें a मिलता है D t (Zipper t a), जो कि a के लिए आवश्यक संदर्भ है Zipper t (Zipper t a)

Comonadउदाहरण तो बस है

instance Diff t => Comonad (Zipper t) where
    extract   = here
    duplicate = around

व्युत्पन्न के Diffशब्दकोश को कैप्चर करने के लिए कुछ अतिरिक्त प्लंबिंग की आवश्यकता होती है, जो Data.Constraint के साथ या संबंधित उत्तर में प्रस्तुत विधि के संदर्भ में किया जा सकता है।

around :: Diff t => Zipper t a -> Zipper t (Zipper t a)
around z = Zipper (withDict d' (fmap (\z' -> Zipper (up z') (here z')) (down (diff z)))) z
    where
        d' = ddiff . p' $ z
        p' :: Zipper t x -> Proxy t
        p' = const Proxy 

इस के साथ चारों ओर बेवकूफ बनाना यह बहुत अच्छा काम करता है: gist.github.com/tel/fae4f90f47a9eda0373b । मैं यह देखने के लिए साफ-सुथरा हूं कि क्या मैं कस्टम जिपर्स को जमीन पर चला सकता हूं और फिर स्वचालित aroundएस पाने के लिए इसका उपयोग कर सकता हूं ।
जे। अब्राहमसन

2
पहली aroundभी टाइपकेक around :: (Diff t, Diff (D t)) => Zipper t a -> Zipper t (Zipper t a)और कोई ddiffविधि नहीं है, और इसी तरह Comonadउदाहरण के लिए, इसलिए दो बार भिन्नता पर्याप्त लगती है।
अर्जन जोहान्सन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.