2016-12-10 17 views
6

मैं हास्केल के लिए अपेक्षाकृत नया हूं और बिफुनक्टरों की उपयोगिता को समझने में परेशानी है। मुझे लगता है कि मैं उन्हें सिद्धांत में समझता हूं: उदाहरण के लिए कहें कि अगर मैं एक प्रकार से मैप करना चाहता हूं जो कई ठोस प्रकारों को सारणीबद्ध करता है, जैसे कि या तो, शायद मुझे उन्हें एक बिफुनक्टर में encapsulate करने की आवश्यकता होगी। लेकिन एक ओर, उन उदाहरणों को विशेष रूप से विकसित किया जाता है, और दूसरी तरफ ऐसा लगता है कि आप रचना के माध्यम से उसी कार्यक्षमता को प्राप्त कर सकते हैं।इसके लिए उपयोग किए जाने वाले बिफुनक्टर क्या हैं जो मज़दूरों को लिखकर हासिल नहीं किए जा सकते हैं?

उदाहरण के तौर पर, मैं इस कोड को जेरेमी गिब्न्स और ब्रूनो सी डी द्वारा The Essence of the Iterator Pattern में मिला। एस ओलिविएरा:

import Data.Bifunctor 

data Fix s a = In {out::s a (Fix s a) } 

map' :: Bifunctor s => (a -> b) -> Fix s a -> Fix s b 
map' f = In . bimap f (map' f) . out 

fold' :: Bifunctor s => (s a b -> b) -> Fix s a -> b 
fold' f = f . bimap id (fold' f) . out 

unfold' :: Bifunctor s => (b -> s a b) -> b -> Fix s a 
unfold' f = In . bimap id (unfold' f) . f 

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

उत्तर

8

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

class Bifunctor f where 
    bimap :: (a -> c) -> (b -> d) -> f a b -> f c d 

Bifunctor रों * -> * -> * का एक प्रकार है और दोनों मानकों covariantly से अधिक मैप किया जा सकता। इसकी तुलना नियमित रूप से Functor एस से करें, जिसमें केवल एक पैरामीटर (f :: * -> *) है जिसे कॉन्वरेन्टली मैप किया जा सकता है।

उदाहरण के लिए, Either के सामान्य Functor उदाहरण के बारे में सोचें। यह आपको दूसरे प्रकार के पैरामीटर पर fmap पर अनुमति देता है - Right मान मैप किए जाते हैं, Left मान वे रहते हैं।

instance Functor (Either a) where 
    fmap f (Left x) = Left x 
    fmap f (Right y) = Right (f y) 

हालांकि, इसकी Bifunctor उदाहरण आप योग के दोनों हिस्सों को मैप करने के लिए अनुमति देता है।

instance Bifunctor Either where 
    bimap f g (Left x) = Left (f x) 
    bimap f g (Right y) = Right (g y) 
इसी तरह tuples के लिए

: (,) के Functor उदाहरण आप केवल दूसरे घटक मैप करने के लिए अनुमति देता है, लेकिन Bifunctor आप दोनों भागों मैप करने के लिए अनुमति देता है।

instance Functor ((,) a) where 
    fmap f (x, y) = (x, f y) 

instance Bifunctor (,) where 
    bimap f g (x, y) = (f x, g y) 

ध्यान दें कि Maybe, जो आप उल्लेख किया है, bifunctors के ढांचे में फिट नहीं करता है, क्योंकि यह केवल एक पैरामीटर है।


Fix के सवाल पर, एक bifunctor की नियत बिन्दु आप इस तरह के सबसे अधिक कंटेनर की तरह संरचनाओं के रूप में जो एक functorial प्रकार पैरामीटर है पुनरावर्ती प्रकार, चिह्नित करने के लिए अनुमति देता है। चलिए एक उदाहरण के रूप में सूचियों का उपयोग करते हैं।

newtype Fix f = Fix { unFix :: f (Fix f) } 

data ListF a r = Nil_ | Cons_ a r deriving Functor 
type List a = Fix (ListF a) 

, मानक functorial Fix का प्रयोग के रूप में मैं ऊपर है, वहाँ List के लिए Functor का एक उदाहरण का कोई सामान्य व्युत्पत्ति है, क्योंकि Fix के बारे में List के a पैरामीटर में कुछ भी पता नहीं है। यही है, मैं instance Something f => Functor (Fix f) जैसे कुछ भी नहीं लिख सकता क्योंकि Fix में गलत प्रकार है।मैं सूचियों के लिए एक map हाथ क्रैंक करने के लिए है, शायद cata का उपयोग कर:

map :: (a -> b) -> List a -> List b 
map f = cata phi 
    where phi Nil_ = Fix Nil_ 
      phi Cons_ x r = Fix $ Cons_ (f x) r 

Fix की bifunctorial संस्करण Functor का एक उदाहरण की अनुमति देता है। FixFix f a की रिकर्सिव घटना में प्लग करने के लिए बाईफंक्टर के पैरामीटर में से एक का उपयोग करता है और दूसरा परिणामस्वरूप डाटाटाइप के फंक्शनल पैरामीटर के लिए खड़ा होता है।

newtype Fix f a = Fix { unFix :: f a (Fix f a) } 

instance Bifunctor f => Functor (Fix f) where 
    fmap f = Fix . bimap f (fmap f) . unFix 

तो हम लिख सकते हैं:

deriveBifunctor ''ListF 

type List = Fix ListF 

और मिल Functor उदाहरण मुक्त करने के लिए:

map :: (a -> b) -> List a -> List b 
map = fmap 
बेशक

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

+1

क्या मैं पूछ सकता हूं कि प्रोग्रामिंग भाषाओं के किस तरह के काम में? क्या आप कुछ सुलभ कर सकते हैं? मैं सोच रहा हूं कि 'क्लास कोविरिएंट (एन :: नेट) पी' जैसे कुछ को परिभाषित करना संभव हो सकता है कि 'पी' अपने' एनएच 'पैरामीटर में कॉन्वर्सेंट है, लेकिन मुझे पूरा यकीन नहीं है कि यह कैसा दिखता है । – dfeuer

+0

स्पष्ट स्पष्टीकरण के लिए धन्यवाद! मैं बैफनक्टरों को अनावश्यक वाक्य रचनात्मक चीनी के रूप में सोच रहा था, लेकिन दो मानकों को लेने के लिए मानचित्र को अधिभारित करने का आपका उदाहरण दर्शाता है कि वे वास्तव में कितने सरल रूप से अर्थात् हैं। हालांकि, अंत में आपकी टिप्पणी में आप जिस चीज का जिक्र कर रहे थे, उससे भी मैं चिंतित हूं। ओकैमल में पॉलिमॉर्फिक फ़ैक्टर नहीं हैं? –

+0

@ डीफ्यूअर ओह, मैं सिर्फ "ब्रह्मांड" के नक्षत्र के संदर्भ में था - स्टाइल डेटाटाइप विवरण जो डीटी भाषाओं में विकसित किए गए हैं। –

संबंधित मुद्दे

 संबंधित मुद्दे