2011-12-22 14 views
7

प्रसंग: मैं एक त्रुटि इकाई है कि यह भी चेतावनी की एक सूची का ट्रैक रखता है, कुछ इस तरह का निर्माण करने के कोशिश कर रहा हूँ:अस्तित्व प्रकार और इकाई ट्रांसफार्मर

data Dangerous a = forall e w. (Error e, Show e, Show w) => 
    Dangerous (ErrorT e (State [w]) a) 

यानी Dangerous a(Either e a, [w]) जहां e में जिसके परिणामस्वरूप एक ऑपरेशन है एक शोषक त्रुटि है और w दिखाया जा सकता है।

समस्या यह है कि, मैं वास्तव में चीज़ को चलाने के लिए प्रतीत नहीं कर सकता, क्योंकि ज्यादातर मैं अस्तित्वहीन प्रकारों को अच्छी तरह समझ नहीं पा रहा हूं। का निरीक्षण करें:

runDangerous :: forall a e w. (Error e, Show e, Show w) => 
    Dangerous a -> (Either e a, [w]) 
runDangerous (Dangerous f) = runState (runErrorT f) [] 

यह संकलन नहीं करता, क्योंकि:

Could not deduce (w1 ~ w) 
from the context (Error e, Show e, Show w) 
... 
`w1' is a rigidtype variable bound by 
    a pattern with constructor 
    Dangerous :: forall a e w. 
       (Error e, Show e, Show w) => 
       ErrorT e (State [w]) a -> Dangerous a 
... 
`w' is a rigid type variable bound by 
    the type signature for 
    runDangerous :: (Error e, Show e, Show w) => 
        Dangerous a -> (Either e a, [w]) 

मैं हार रहा हूँ। डब्ल्यू 1 क्या है? हम क्यों नहीं ले सकते कि यह ~ w है?

उत्तर

12

संभवतः एक अस्तित्व संभवतः आप यहां नहीं चाहते हैं; या w पर Dangerous a मान में वास्तविक प्रकारों को "निरीक्षण" करने का कोई तरीका नहीं है, इसलिए आप Error और Show द्वारा दिए गए संचालन तक पूरी तरह से सीमित हैं।

दूसरे शब्दों में, केवल एक चीज के बारे में क्या आप जानते हैं w है कि आप इसे एक String में बदल सकते हैं, तो यह रूप में अच्छी तरह सिर्फ एक String (अनदेखी चीजों को आसान बनाने के पूर्वता), और केवल एक चीज के बारे में आप e जानते हो सकता है यह है कि आप इसे String में बदल सकते हैं, आप इसमें String एस बदल सकते हैं, और आपके पास इसका विशिष्ट मान है (noMsg)। जोर देने या जांचने का कोई तरीका नहीं है कि ये प्रकार किसी अन्य के समान हैं, इसलिए एक बार जब आप उन्हें Dangerous में डाल देते हैं, तो उन प्रकारों की किसी भी विशेष संरचना को पुनर्प्राप्त करने का कोई तरीका नहीं है।

क्या त्रुटि संदेश कह रहा है यह है कि अनिवार्य रूप से, runDangerous दावों के लिए अपने प्रकार है कि आप के लिए एक (Either e a, [w]) में एक Dangerous बदल सकते हैं किसी भीe और w प्रासंगिक उदाहरणों को है। यह स्पष्ट रूप से सत्य नहीं है: आप केवल को उस प्रकार में एकe और w के विकल्प में बदल सकते हैं: जिसे वह बनाया गया था। w1 सिर्फ इसलिए है क्योंकि आपके Dangerous प्रकार को एक प्रकार परिवर्तनीय w के साथ परिभाषित किया गया है, और इसलिए runDangerous है, इसलिए जीएचसी नाम संघर्ष से बचने के लिए उनमें से एक का नाम बदलता है।

प्रकार आप runDangerous देने की आवश्यकता इस तरह दिखता है:

runDangerous 
    :: (forall e w. (Error e, Show e, Show w) => (Either e a, [w]) -> r) 
    -> Dangerous a -> r 

जो एक समारोह जो इतने लंबे समय पहले की तरह e की किसी भी विकल्प और w के लिए प्रकार (Either e a, [w]) के एक मूल्य स्वीकार करेंगे दिया दिए गए उदाहरण, और Dangerous a, उस कार्य के परिणाम का उत्पादन करता है। अपने सिर को चारों ओर लेना मुश्किल है!

कार्यान्वयन है के रूप में सरल

runDangerous f (Dangerous m) = f $ runState (runErrorT m) [] 

जो अपने संस्करण के लिए एक छोटी सी परिवर्तन है। यदि यह आपके लिए काम करता है, तो बढ़िया; लेकिन मुझे संदेह है कि जो कुछ भी आप करने की कोशिश कर रहे हैं उसे हासिल करने का एक सही तरीका है।

ध्यान दें कि runDangerous के प्रकार को व्यक्त करने के लिए आपको {-# LANGUAGE RankNTypes #-} की आवश्यकता होगी। फिर, आप अपने परिणाम प्रकार के लिए एक और अस्तित्व को परिभाषित कर सकते हैं:

data DangerousResult a = forall e w. (Error e, Show e, Show w) => 
    DangerousResult (Either e a, [w]) 

runDangerous :: Dangerous a -> DangerousResult a 
runDangerous (Dangerous m) = DangerousResult $ runState (runErrorT m) [] 

और case साथ परिणाम निकालने, लेकिन आप सावधान रहना होगा, या GHC शिकायत आप e या w बच जाने की है कि शुरू कर देंगे - जो runDangerous के दूसरे रूप में अपर्याप्त रूप से पॉलिमॉर्फिक फ़ंक्शन को पारित करने का प्रयास करने के बराबर है; यानी e और w पर runDangerous गारंटी के प्रकार से अधिक बाधाओं की आवश्यकता होती है।

+0

क्या ऐसा करने के लिए कोई बेहतर (या अधिक मूर्खतापूर्ण) तरीका है? मैं वास्तव में सिर्फ एक त्रुटि मोनड चाहता हूं जो कुछ चेतावनियों के साथ आता है। – So8res

+0

क्यों न केवल 'खतरनाक ई डब्ल्यू ए' के ​​रूप में परिभाषित करें? यहां अस्तित्व की कोई आवश्यकता नहीं है, अगर मैं समझता हूं कि आप क्या हासिल करने की कोशिश कर रहे हैं (जो मुझे शायद नहीं हो सकता है)। – ehird

+0

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

1

ठीक है, मुझे लगता है कि मैं पता लगा है कि मैं क्या करने के बाद लड़खड़ा गया था: (। instance Monad Dangerous और data DangerousT मदद भी)

data Failure = forall e. (Error e, Show e) => Failure e 

data Warning = forall w. (Show w) => Warning w 

class (Monad m) => Errorable m where 
    warn :: (Show w) => w -> m() 
    throw :: (Error e, Show e) => e -> m() 

instance Errorable Dangerous where 
    warn w = Dangerous (Right(), [Warning w]) 
    throw e = Dangerous (Left $ Failure e, []) 

यह आप निम्नलिखित कोड की अनुमति देता है:

foo :: Dangerous Int 
foo = do 
    when (badThings) (warn $ BadThings with some context) 
    when (worseThings) (throw $ BarError with other context) 

data FooWarning = BadThings FilePath Int String 
instance Show FooWarning where 
... 

और फिर अपने मुख्य मॉड्यूल में आप Show Failure, Error Failure, और Show Warning के कस्टम उदाहरणों को परिभाषित कर सकते हैं nd उदाहरण

instance Show Warning where show (Warning s) = "WARNING: " ++ show s 
instance Show Failure where ... 

let (result, warnings) = runDangerous function 
in ... 

कौन सा, मेरी राय में, त्रुटियों और चेतावनियों को संभालने के लिए एक बहुत अच्छा तरीका है के लिए अपने त्रुटि संदेश फ़ॉर्मेट करने के लिए एक केंद्रीकृत तरीका है,। मेरे पास एक कामकाजी मॉड्यूल है जो इस तरह कुछ है, अब मैं इसे पॉलिश करने के लिए बंद हूं और शायद इसे हैक पर डाल सकता हूं। सुझावों की सराहना की।

+1

क्षमा करें, लेकिन 'डेटा चेतावनी = कुल मिलाकर डब्ल्यू। (डब्ल्यू दिखाएं) => चेतावनी w' डेटा के बराबर है चेतावनी = चेतावनी स्ट्रिंग'; चेतावनी मान पर आपके पास 'चेतावनी' फ़ंक्शन कॉल 'शो' भी हो सकता है। अस्तित्व * वास्तव में आप जो चाहते हैं * नहीं हैं। – ehird

+1

मुझे सराहना है कि आप एक शांत प्रकार की सिस्टम सुविधा लागू करने की कोशिश कर रहे हैं, लेकिन इसका एकमात्र प्रभाव यह है कि कोड को किसी भी कार्यक्षमता को जोड़ने के बिना कोड को और जटिल बनाना है। – ehird

+0

व्याख्या करने में सहायता के लिए यहां कुछ और सामग्री दी गई है: [1] (http://www.haskell.org/haskellwiki/FAQ#How_do_I_make_a_list_with_elements_of_different_types.3F), [2] (http://lukepalmer.wordpress.com/2010/01/ 24/हैकेल-एंटीपेटर्न-अस्तित्व-टाइपक्लास /) - मैंने वास्तव में एक अच्छा स्पष्टीकरण देखा है कि 'डेटा Foo = forall a जैसे कुछ का उपयोग क्यों न करें। (एक दिखाएं) => फू ए 'भी, लेकिन दुर्भाग्य से अभी लिंक नहीं मिल रहा है। – ehird

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