2015-04-06 8 views
8

लें वर्ग Functor:टाइप क्लास में टाइप कंस्ट्रक्टर का उपयोग करने का कोई फायदा? उदाहरण के लिए

class Functor a 
instance Functor Maybe 

यहाँ Maybe एक प्रकार निर्माता है। सबसे पहले मल्टी पैरामीटर प्रकार वर्गों का उपयोग

,:

लेकिन हम इस दो अन्य तरीकों से कर सकते हैं

class MultiFunctor a e 
instance MultiFunctor (Maybe a) a 

प्रकार परिवारों दूसरे का उपयोग कर:

class MonoFunctor a 
instance MonoFunctor (Maybe a) 

type family Element 
type instance Element (Maybe a) a 

अब वहाँ एक स्पष्ट है दो बाद के तरीकों का लाभ, अर्थात् यह हमें इस तरह की चीजों को करने की अनुमति देता है:

instance Text Char 

या:

instance Text 
type instance Element Text Char 

तो हम monomorphic कंटेनर के साथ काम कर सकते हैं।

दूसरा फायदा यह है कि हम उन प्रकारों के उदाहरण बना सकते हैं जिनमें अंतिम पैरामीटर के रूप में टाइप पैरामीटर नहीं है। हम एक Either शैली प्रकार बनाने लेकिन प्रकार के गलत तरीके से चारों ओर डाल कहते हैं कि चलो:

data Silly t errorT = Silly t errorT 

instance Functor Silly -- oh no we can't do this without a newtype wrapper 

जबकि

instance MultiFunctor (Silly t errorT) t 

ठीक काम करता है और

instance MonoFunctor (Silly t errorT) 
type instance Element (Silly t errorT) t 

भी अच्छा है।

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

उत्तर

4

ठीक है, एक बात के लिए पारंपरिक मज़ेदार वर्ग बस इतना आसान है। यह अकेला ही इसे पसंद करने का एक वैध कारण है, भले ही यह हास्केल and not Python है। और यह गणितीय विचार को भी बेहतर बनाता है कि एक मज़ेदार क्या होता है: अतिरिक्त संपत्ति (->Constraint) के साथ ऑब्जेक्ट्स से ऑब्जेक्ट्स () में मैपिंग, प्रत्येक (forall (a::*) (b::*)) morphism (a->b) संबंधित पर एक मोर्फिज्म पर उठाया जाता है ऑब्जेक्ट मैप किया गया (-> f a->f b)।
इनमें से कोई भी कक्षा के * -> * -> Constraint संस्करण, या इसके टाइपफमिलियों समकक्ष में बहुत स्पष्ट रूप से देखा जा सकता है।

अधिक व्यावहारिक खाते पर, हाँ, ऐसी चीजें भी हैं जो आप केवल (*->*)->Constraint संस्करण के साथ कर सकते हैं।

विशेष रूप से, क्या इस बाधा आप तुरंत गारंटी देता है कि सभी हास्केल प्रकार वैध वस्तुओं आप functor में डाल सकते हैं, जबकि MultiFunctor के लिए आप हर संभव निहित प्रकार, एक के बाद एक जांच करने की आवश्यकता हो रहा है।कभी कभी कि अभी संभव नहीं है (या यह क्या है?) जब आप असीम कई प्रकार के ऊपर मानचित्रण कर रहे हैं,:

data Tough f a = Doable (f a) 
       | Tough (f (Tough f (a, a))) 

instance (Applicative f) = Semigroup (Tough f a) where 
    Doable x <> Doable y = Tough . Doable $ (,)<$>x<*>y 
    Tough xs <> Tough ys = Tough $ xs <> ys 
    -- The following actually violates the semigroup associativity law. Hardly matters here I suppose... 
    xs <> Doable y = xs <> Tough (Doable $ fmap twice y) 
    Doable x <> ys = Tough (Doable $ fmap twice x) <> ys 

twice x = (x,x) 

ध्यान दें कि यह f की Applicative उदाहरण का उपयोग करता है न सिर्फ a प्रकार पर, लेकिन यह भी मनमाना पर इसके tuples। मैं नहीं देख सकता कि आप इसे MultiParamTypeClasses - या TypeFamilies-आधारित आवेदक वर्ग के साथ कैसे व्यक्त कर सकते हैं। (यदि आप Tough एक उपयुक्त GADT बनाने के लिए, लेकिन यह है कि बिना संभव हो सकता है ... शायद नहीं।)

Btw, इस उदाहरण शायद के रूप में बेकार नहीं है, यह – देखो यह मूल रूप से लंबाई के केवल पढ़ने के लिए वैक्टर व्यक्त करता है हो सकता है के रूप में 2 एन एक monadic राज्य में।

+0

क्या आप परंपरागत दृष्टिकोण का उपयोग कर लेंस के बारे में और अधिक समझा सकते हैं, विशेष रूप से टाइप परिवारों के साथ क्या हासिल नहीं किया जा सकता है, शायद एक साधारण उदाहरण के साथ? – Clinton

+0

@ क्लिंटन: मैंने इसके बारे में सोचा; 'लेंस' _could_ शायद' * -> * -> बाधा 'फंक्चर क्लास के साथ व्यक्त किया जा सकता है (यद्यपि बल्कि अधिक अजीब तरह से)। मैंने एक अलग उदाहरण जोड़ा। – leftaroundabout

2

विस्तारित संस्करण वास्तव में अधिक लचीला है। इसका इस्तेमाल किया गया था उदा। ओलेग Kiselyov द्वारा restricted monads परिभाषित करने के लिए। मोटे तौर पर, आप

class MN2 m a where 
    ret2 :: a -> m a 

class (MN2 m a, MN2 m b) => MN3 m a b where 
    bind2 :: m a -> (a -> m b) -> m b 

हो सकता है की अनुमति देता है इकाई उदाहरणों a और b से अधिक parametrized जा करने के लिए। की तुलना में क्योंकि उस Ord बाधा के, हम अप्रतिबंधित monads का उपयोग कर Monad Set.Set को परिभाषित करने में असमर्थ हैं

import Data.Set as Set 

instance MN2 Set.Set a where 
    -- does not require Ord 
    return = Set.singleton 

instance Prelude.Ord b => MN3 SMPlus a b where 
    -- Set.union requires Ord 
    m >>= f = Set.fold (Set.union . f) Set.empty m 

नोट: क्योंकि आप कुछ अन्य वर्ग के सदस्यों को उन प्रकार सीमित कर सकते हैं तो यह उपयोगी है। दरअसल, मोनैड क्लास को मोनैड को सभी प्रकारों पर प्रयोग करने योग्य होने की आवश्यकता होती है।

यह भी देखें: parameterized (indexed) monad

+2

कहने के लायक है कि अभी कुछ समय के लिए, प्रतिबंधित मोनैड इत्यादि 'ConstraintKinds' का उपयोग करके एक और अधिक सरल तरीके से किया जा सकता है। यदि मैं [इस के मेरे कार्यान्वयन का विज्ञापन कर सकता हूं] (http://hackage.haskell.org/package/constrained-categories) ... – leftaroundabout

12

आपके प्रस्ताव मौजूदा Functor परिभाषा के बारे में कुछ महत्वपूर्ण जानकारी अनदेखा करते हैं क्योंकि आपने कक्षा के सदस्य कार्य के साथ क्या होगा, यह जानने के विवरण के माध्यम से काम नहीं किया है।

class MultiFunctor a e where 
    mfmap :: (e -> ??) -> a -> ???? 

instance MultiFunctor (Maybe a) a where 
    mfmap = ??????? 

fmap इस समय का एक महत्वपूर्ण संपत्ति है कि इसके पहले तर्क प्रकार बदल सकता है। fmap show :: (Functor f, Show a) => f a -> f String। आप इसे दूर फेंक नहीं सकते हैं, या आप fmap के अधिकांश मूल्य खो देते हैं। तो सच में, MultiFunctor की तरह अधिक देखने के लिए की आवश्यकता होगी ... इस

class MultiFunctor s t a b | s -> a, t -> b, s b -> t, t a -> s where 
    mfmap :: (a -> b) -> s -> t 

instance (a ~ c, b ~ d) => MultiFunctor (Maybe a) (Maybe b) c d where 
    mfmap _ Nothing = Nothing 
    mfmap f (Just a) = Just (f a) 

नोट कितना अविश्वसनीय रूप से जटिल अनुमान पास कम से कम संभव करने के लिए बनाने की कोशिश करने के लिए बन गया है। सभी कार्यात्मक निर्भरताएं जगह पर एनोटेटिंग प्रकारों के बिना इंस्टेंस चयन की अनुमति देने के लिए मौजूद हैं। (मैंने वहां कुछ संभावित कार्यात्मक निर्भरताओं को याद किया होगा!) उदाहरण ने उदाहरण के चयन को अधिक भरोसेमंद होने की अनुमति देने के लिए कुछ पागल प्रकार की समानता बाधाओं को जन्म दिया। और सबसे बुरा हिस्सा यह है - fmap की तुलना में तर्क के लिए अभी भी बदतर गुण हैं।

मेरे पिछले उदाहरण मान लें कि अस्तित्व में नहीं था, मैं इस तरह एक उदाहरण लिख सकते हैं:

instance MultiFunctor (Maybe Int) (Maybe Int) Int Int where 
    mfmap _ Nothing = Nothing 
    mfmap f (Just a) = Just (if f a == a then a else f a * 2) 

यह टूट गया है, ज़ाहिर है - लेकिन यह एक नया तरीका है कि पहले भी संभव नहीं था में टूट गया है। वास्तव मेंFunctor की परिभाषा का महत्वपूर्ण हिस्सा यह है कि a और bfmap में उदाहरण परिभाषा में कहीं भी दिखाई नहीं देते हैं।बस वर्ग को देख प्रोग्रामर कि fmap नहीं कर सकते हैं के व्यवहार प्रकार a और b पर निर्भर बताने के लिए पर्याप्त है। आपको वह गारंटी मुफ्त में मिलती है। आपको विश्वास करने की आवश्यकता नहीं है कि उन मामलों को सही ढंग से लिखा गया था।

क्योंकि fmap आपको मुफ्त में गारंटी देता है, आपको किसी उदाहरण को परिभाषित करते समय Functor कानूनों को भी जांचने की आवश्यकता नहीं है। कानून fmap id x == x को जांचना पर्याप्त है। दूसरा कानून मुफ्त में आता है जब पहला कानून साबित होता है। लेकिन उस टूटे mfmap के साथ मैंने अभी प्रदान किया है, mfmap id x == x सत्य है, भले ही दूसरा कानून नहीं है।

mfmap के implementer के रूप में, आप को साबित करने के लिए अपने कार्यान्वयन सही है क्या करने के लिए और अधिक काम किया है। इसके उपयोगकर्ता के रूप में, आपको कार्यान्वयन की शुद्धता में अधिक विश्वास करना होगा, क्योंकि टाइप सिस्टम उतना गारंटी नहीं दे सकता है।

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

+0

वाह, मुझे यह भी एहसास नहीं हुआ कि अगर हम इस कक्षा को बाध्य नहीं करते हैं तो हम कितनी जल्दी बाध्य हो जाते हैं इसे '* -> *' प्रकार के साथ कार्यान्वित नहीं किया गया (मैंने सोचा कि 'mfmap :: मल्टीफंक्टर बीडी => (ए-> बी) -> ई-> डी' चाल करेगा, लेकिन कोई रास्ता नहीं)। – leftaroundabout

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

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