2010-07-09 13 views
46

मेरे पास एक टाइपक्लास MyClass है, और इसमें एक फ़ंक्शन है जो String उत्पन्न करता है। मैं Show का उदाहरण देने के लिए इसका उपयोग करना चाहता हूं, ताकि मैं MyClass को show लागू करने वाले प्रकारों को पारित कर सकूं। अब तक मेरे पास है,मैं कैसे लिखूं, "अगर टाइपक्लास एक है, तो यह भी परिभाषा द्वारा बी का एक उदाहरण है।"

class MyClass a where 
    someFunc :: a -> a 
    myShow :: a -> String 

instance MyClass a => Show a where 
    show a = myShow a 

जो त्रुटि Constraint is no smaller than the instance head. मैं भी करने की कोशिश की है,

class MyClass a where 
    someFunc :: a -> a 
    myShow :: a -> String 

instance Show (MyClass a) where 
    show a = myShow a 

जो त्रुटि देता है, Class MyClass 'एक type` रूप में इस्तेमाल किया।

मैं हास्केल में इस संबंध को सही ढंग से कैसे व्यक्त कर सकता हूं? धन्यवाद।

मुझे यह जोड़ना चाहिए कि मैं MyClass के विशिष्ट उदाहरणों के साथ इसका पालन करना चाहता हूं जो उनके प्रकार के आधार पर विशिष्ट तारों को उत्सर्जित करता है। उदाहरण के लिए,

data Foo = Foo 
data Bar = Bar 

instance MyClass Foo where 
    myShow a = "foo" 

instance MyClass Bar where 
    myShow a = "bar" 

main = do 
    print Foo 
    print Bar 
+1

एफवाईआई मैंने इस प्रश्न को फिर से सुना क्योंकि यह अभी तक संक्षिप्त, स्पष्ट और अच्छी तरह से गठित है। एक मॉडल सवाल, और यदि संभव हो तो मैं इसे फिर से पढ़ूंगा। –

उत्तर

28

(संपादित करें: भावी पीढ़ी के लिए शरीर छोड़ रहा है, लेकिन वास्तविक समाधान के लिए अंत करने के लिए कूद) "। बाधा उदाहरण सिर से भी छोटा होता है" घोषणा instance MyClass a => Show a में

, चलो की जांच त्रुटि जाने बाधा '=>' के बाईं ओर टाइप क्लास बाधा है, इस मामले में MyClass a। "उदाहरण सिर" कक्षा के बाद सब कुछ है जिसके लिए आप एक उदाहरण लिख रहे हैं, इस मामले में a (Show के दाईं ओर)। जीएचसी में टाइप अनुमान नियमों में से एक के लिए यह आवश्यक है कि बाधा के सिर के मुकाबले कम कन्स्ट्रक्टर और चर हो। यह 'Paterson Conditions' कहलाता है इसका हिस्सा है। ये गारंटी के रूप में मौजूद हैं कि प्रकार की जांच समाप्त हो जाती है।

इस मामले में, बाधा बिल्कुल सिर के समान ही है, यानी a, इसलिए यह इस परीक्षण में विफल रहता है। आप UndecidableInstances सक्षम करके पैटरसन स्थिति जांच को हटा सकते हैं, {-# LANGUAGE UndecidableInstances #-} प्रगामा के साथ सबसे अधिक संभावना है।

इस मामले में, आप अनिवार्य रूप से को Show कक्षा के लिए टाइपक्लास समानार्थी के रूप में उपयोग कर रहे हैं। इस तरह के वर्ग समानार्थी बनाना UndecidableInstances एक्सटेंशन के लिए कैननिकल उपयोगों में से एक है, ताकि आप इसे सुरक्षित रूप से यहां उपयोग कर सकें।

'अनिश्चित' का अर्थ है कि जीएचसी साबित नहीं कर सकता कि टाइपशेकिंग समाप्त हो जाएगी। यद्यपि यह खतरनाक लगता है, अविश्वसनीय इंस्टेंस को सक्षम करने से सबसे खराब हो सकता है कि संकलक लूप होगा, अंततः ढेर को समाप्त करने के बाद समाप्त हो जाएगा। यदि यह संकलित करता है, तो स्पष्ट रूप से टाइपकेकिंग समाप्त हो जाती है, इसलिए कोई समस्या नहीं है। खतरनाक विस्तार IncoherentInstances है, जो यह लगता है के रूप में बुरा है।

संपादित करें: इस दृष्टिकोण से संभव बनाया एक और समस्या इस स्थिति से उत्पन्न होती है:

instance MyClass a => Show a where 

data MyFoo = MyFoo ... deriving (Show) 

instance MyClass MyFoo where 

अब MyFoo के लिए शो के दो उदाहरणों, पाने खंड से एक और MyClass उदाहरण के लिए एक है। संकलक यह तय नहीं कर सकता कि किस का उपयोग करना है, इसलिए यह एक त्रुटि संदेश के साथ जमानत देगा। यदि आप MyClass प्रकारों के उदाहरण बनाने की कोशिश कर रहे हैं, तो आप पहले से मौजूद Show उदाहरणों को नियंत्रित नहीं करते हैं, तो आपको पहले से मौजूद शो उदाहरणों को छुपाने के लिए नए प्रकार का उपयोग करना होगा। MyClass उदाहरणों के बिना भी प्रकार अभी भी संघर्ष क्योंकि परिभाषा instance MyClass => Show a क्योंकि परिभाषा वास्तव में सभी संभव a के लिए एक कार्यान्वयन प्रदान करता है (संदर्भ की जांच बाद में आता है, इसके उदाहरण चयन के साथ शामिल नहीं)

ताकि त्रुटि संदेश और कैसे UndecidableInstances है यह दूर चला जाता है। दुर्भाग्यवश, एडवर्ड Kmett बताते हैं कि वास्तविक कोड में उपयोग करने में बहुत परेशानी है। MyClass बाधा पहले से ही Show बाधा निर्दिष्ट करने से बचने के लिए मूल प्रेरणा थी। यह देखते हुए कि, मैं show के बजाय MyClass से myShow का उपयोग करता हूं। आपको Show बाधा की आवश्यकता नहीं होगी।

+0

धन्यवाद, अगर मैं UndecidableInstances को सक्षम करता हूं तो यह वास्तव में काम करता है। अजीब। क्या यह टाइपक्लास प्रणाली का दुरुपयोग है? मैं ज्यादातर "MyClass" लेने वाले कार्यों के लिए बाधा "शो" निर्दिष्ट करने से बचने की कोशिश कर रहा था और इसे दिखाने की भी आवश्यकता थी। चूंकि सभी MyClass उदाहरणों में उनके साथ एक स्ट्रिंग होनी चाहिए, इसलिए मुझे लगता है कि शो स्वचालित रूप से व्युत्पन्न दिखाना एक तरीका होगा। हालांकि, गूढ़ भाषा एक्सटेंशन को सक्षम करने के बजाय, जहां आवश्यक हो, स्पष्ट रूप से निर्दिष्ट करके समस्या से बचने के लिए बेहतर लगता है। तुम क्या सोचते हो? – Steve

+3

मैं सभी कार्यों में दिखाने के बजाय myShow का उपयोग करूंगा ताकि आपको शो बाधा की आवश्यकता न हो। उदाहरण परिभाषाओं में आप उचित रूप से 'myShow = show' लिख सकते हैं। मैं संपादन में वर्णित समस्या के कारण 'उदाहरण MyClass a => एक दिखाएँ' लिखने से बचूंगा। डेव हिनटन का समाधान UndecidableInstances से बेहतर है, लेकिन मुझे नहीं लगता कि आपको सुविधा के लिए सुपरक्लस बाधा नहीं जोड़नी चाहिए। –

+2

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

2

आप इसे संकलन कर सकते हैं, लेकिन नहीं हास्केल 98 के साथ, आप कुछ भाषा एक्सटेंशन सक्षम करने के लिए है: वहाँ उदाहरण घोषणा में संदर्भ अनुमति है

{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE UndecidableInstances #-} 
-- at the top of your file 

लचीला उदाहरणों। मुझे वास्तव में अनिश्चितता के अर्थों का अर्थ नहीं पता है, लेकिन मैं जितना कर सकता हूं उससे बचूंगा।

+0

मैं यह मान रहा था कि जीएचसी सोचता है कि यह अनावश्यक है शायद एक समस्या है। अगर कोई यह समझा सकता है कि यह क्यों अनावश्यक है कि यह बहुत अच्छा होगा! – Steve

+3

यदि आप UndecidableInstances को सक्षम करते हैं, तो फ्लेक्सिबल इंस्टेंस अनिवार्य है। –

5

मुझे लगता है कि यह बेहतर होगा यह दूसरी तरह के आसपास करने के लिए:

class Show a => MyClass a where 
    someFunc :: a -> a 

myShow :: MyClass a => a -> String 
myShow = show 
+0

... सिवाय इसके कि, ओपी को एक कस्टम 'शो' संस्करण घोषित नहीं करने देगा। साथ ही, यह एक प्रकार के लिए 'MyClass' की घोषणा की अनुमति नहीं देगा जो अभी तक एक शो उदाहरण नहीं है)। – BMeph

+4

यहां एहसास करने की बात यह है कि जब आप शो = माईशो कहते हैं, तो आप पहले से ही कस्टम शो रखने की लड़ाई खो चुके हैं क्योंकि किसी प्रकार के दो अलग-अलग शो फ़ंक्शन नहीं हो सकते हैं, और एक प्रकार जो MyClass लागू करता है उसका एक उदाहरण होना चाहिए दिखाएँ (इसे शो के myShow instaid को कॉल करने से कोई फर्क नहीं पड़ता।) यह है कि अगर मैं टी को MyClass का उदाहरण बनना चाहता हूं, तो मुझे अपने शो को लागू करना होगा, अंत में शो के उदाहरण के रूप में यह शो फ़ंक्शन बन जाएगा। तुलनात्मक रूप से लागू करने के लिए इसकी तुलना करें, और उसके बाद MyClass को कार्यान्वित करें, और आप देखें कि केवल अंतर ही फ़ंक्शन का नाम है। – HaskellElephant

+1

मुझे इस समाधान को पसंद है क्योंकि ओपी जिस तरह से अपनी समस्या प्रस्तुत करता है, 'myShow' सभी' MyClass' उदाहरणों के लिए 'शो' के बराबर है क्योंकि' MyClass' 'दिखाएं' (ओपी की इच्छा से) का संकेत देगा। यद्यपि आपको इस मामले में 'myShow' फ़ंक्शन की आवश्यकता नहीं होगी। बस 'शो' का प्रयोग करें। –

53

मैं सख्ती टूटा समाधान से असहमत करना चाहते हैं अब तक बनी हुयी थी।

instance MyClass a => Show a where 
    show a = myShow a 
तरीके के कारण उदाहरण के संकल्प से काम करता है कि करने के लिए

, इस के आसपास चल रहा है के लिए एक बहुत ही खतरनाक उदाहरण है!

इंस्टेंस रिज़ॉल्यूशन प्रत्येक उदाहरण के => के दाएं हाथ की ओर से प्रभावी रूप से मिलान पैटर्न से प्राप्त होता है, पूरी तरह से => के बाईं ओर क्या है इसके संबंध में।

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

यदि आप इसे संकलित करने के लिए {-# LANGUAGE OverlappingInstances, IncoherentInstances #-} इत्यादि चालू करते हैं, तो आप मॉड्यूल को लिखने वाले मॉड्यूल को लिखने के लिए जाते हैं जो इस परिभाषा को प्रदान करते हैं और किसी अन्य शो उदाहरण का उपयोग करने की आवश्यकता होती है। आखिरकार आप इस कोड को पर्याप्त एक्सटेंशन के साथ संकलित करने में सक्षम होंगे, लेकिन दुख की बात यह नहीं होगी कि आपको क्या करना चाहिए!

आपको दिया इसके बारे में सोचते हैं:

instance MyClass a => Show a where 
    show = myShow 

instance HisClass a => Show a where 
    show = hisShow 

जो संकलक लेने चाहिए?

आपका मॉड्यूल केवल इनमें से एक को परिभाषित कर सकता है, लेकिन अंतिम उपयोगकर्ता कोड मॉड्यूल का एक गुच्छा आयात करेगा, न केवल आपके।इसके अलावा, एक और मॉड्यूल को परिभाषित करता है, तो

instance Show HisDataTypeThatHasNeverHeardOfMyClass 

संकलक उसके उदाहरण पर ध्यान न दें और तुम्हारा इस्तेमाल करने की कोशिश करने के लिए अपने अधिकार के भीतर अच्छी तरह से किया जाएगा।

सही जवाब, दुख की बात है, दो चीजें करना है।

प्रत्येक व्यक्ति MyClass के कहने तुम बहुत यांत्रिक परिभाषा

instance MyClass Foo where ... 

instance Show Foo where 
    show = myShow 

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

जब आपके पास बड़ी संख्या में उदाहरण हैं, तो कोड-डुप्लिकेशन से बचने का तरीका (जब वर्ग शो से काफी जटिल है) परिभाषित करना है।

newtype WrappedMyClass a = WrapMyClass { unwrapMyClass :: a } 

instance MyClass a => Show (WrappedMyClass a) where 
    show (WrapMyClass a) = myShow a 

यह उदाहरण प्रेषण के लिए वाहन के रूप में नया प्रकार प्रदान करता है। और फिर

instance Foo a => Show (WrappedFoo a) where ... 
instance Bar a => Show (WrappedBar a) where ... 

, स्पष्ट है, क्योंकि WrappedFoo a और WrappedBar a के लिए प्रकार 'पैटर्न' संबंध तोड़ना है।

base पैकेज में इस मुहावरे के कई उदाहरण चल रहे हैं।

नियंत्रण में। इस कारण से WrappedMonad और WrappedArrow के लिए परिभाषाएं हैं।

आदर्श रूप में आप कहते हैं कि करने में सक्षम हो जाएगा:

instance Monad t => Applicative t where 
    pure = return 
    (<*>) = ap 

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

+1

हां, मैं त्रुटि संदेश को समझाने में इतना पकड़ा गया और क्या अनिश्चितताएं करता है कि मैं पूरी तरह से अपने असली समाधान में डालना भूल गया (एक बहुत छोटी टिप्पणी को छोड़कर)! मैंने इसे शामिल करने के लिए अपनी प्रतिक्रिया संपादित की है। –

+3

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

+0

वैसे, क्या यह कहना सुरक्षित है, "एक टाइपक्लास स्वचालित रूप से (सुरक्षित रूप से) किसी अन्य टाइपक्लास को प्राप्त नहीं कर सकता है, इसे केवल एक (बाध्य होना चाहिए) की आवश्यकता हो सकती है?" – Steve

1

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

{-# LANGUAGE DefaultSignatures #-} 

class MyClass a where 
    someFunc :: a -> Int 

class MyShow a where 
    myShow :: a -> String 
    default myShow :: MyClass a => a -> String 
    myShow = show . someFunc 

instance MyClass Int where 
    someFunc i = i 

instance MyShow Int 

main = putStrLn (myShow 5) 

ध्यान दें कि एकमात्र वास्तविक बॉयलरप्लेट (अच्छी तरह से, पूरे उदाहरण के अलावा) instance MyShow Int तक कम हो गया।

अधिक यथार्थवादी उदाहरण के लिए aeson s ToJSON देखें।

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