2012-08-31 10 views
5

मैं हास्केल का उपयोग कर मूल लेक्सर लिखने की कोशिश कर रहा हूं। डीएफए और एनएफए को लागू करने के लिए, मैंने कक्षाओं एफए और फासेट में कुछ सामान्य कार्यों को रखने का फैसला किया।बहु पैरामीटर कक्षाओं के साथ कोई उदाहरण त्रुटि

-- |A class for defining the common functionality of all finite automatons. 
class FA a b where 
    mutateId :: a -> Int -> a    -- ^Returns a new FA by changing the sId of the input FA. 
    mutateType :: a -> StateType -> a  -- ^Returns a new FA by changing the stateType of the input FA. 
    addTransition :: a -> (b, a) -> a  -- ^Returns a new FA by adding a new transition to the input FA. 


-- |A class for defining the common functionality of all finite automaton states. 
class FA a b => FAState a b where 
    sId :: a -> Int       -- ^An unique identifier for the state(hence the prefix s). 
    sType :: a -> StateType     -- ^The type of the state. 
    sTransitions :: a -> Transitions b a -- ^The transitions that occur from this state. 

जहां,

-- |A type which identifies different types of a FA state. 
data StateType = Start | Normal | Final 
    deriving (Show, Read, Eq) 

-- |A type which represents a list of transitions on input a to b. 
-- Eg. [(Char, DFA)] represents the transition on a Char input. 
type Transitions a b = [(a, b)] 

इसलिए, ख डेटा प्रकार जिसके लिए संक्रमण होते हैं प्रतिनिधित्व करता है। एक डीएफए के लिए, बी = चार, जबकि एक एनएफए के लिए, बी = प्रतीक।

data Symbol = Alphabet Char | Epsilon 
    deriving (Show, Read, Eq) 

DFA और NFA क्रमशः के रूप में परिभाषित कर रहे हैं:

data DFA = DState Int StateType (Transitions Char DFA) 
    deriving (Show, Read) 
data NFA = NState Int StateType (Transitions Symbol NFA) 
    deriving (Show, Read) 

मैं एफए & FAState के कहने परिभाषा के साथ समस्या हो रही है:

instance FA DFA Char where 
    mutateId (DState i ty ts) new_i = DState new_i ty ts 
    mutateType (DState i ty ts) new_ty = DState i new_ty ts 
    addTransition (DState i ty ts) state = DState i ty (state:ts) 

instance FAState DFA Char where 
    sId (DState i t ts) = i 
    sType (DState i t ts) = t 
    sTransitions (DState i t ts) = ts 

instance FA NFA Symbol where 
    mutateId (NState i ty ts) new_i = NState new_i ty ts 
    mutateType (NState i ty ts) new_ty = NState i new_ty ts 
    addTransition (NState i ty ts) state = NState i ty (state:ts) 

instance FAState NFA Symbol where 
    sId (NState i t ts) = i 
    sType (NState i t ts) = t 
    sTransitions (NState i t ts) = ts 

के किसी भी भागने की कोशिश कर पर जिन कार्यों को मुझे कोई उदाहरण त्रुटि नहीं मिलती है:

>>sId egNFA 

<interactive>:15:1: 
    No instance for (FAState NFA b0) 
     arising from a use of `sId' 
    Possible fix: add an instance declaration for (FAState NFA b0) 
    In the expression: sId egNFA 
    In an equation for `it': it = sId egNFA 

मुझे समझ में नहीं आता कि यहां क्या हो रहा है।

उत्तर

7

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

अब, मन में है कि सिद्धांत रखते हुए, क्या आप संकलक पूछा है क्या करने के लिए के बारे में सोचना: आप FAState NFA Symbol का एक उदाहरण दिया गया है, और एक अभिव्यक्ति जो बहुरूपी typeclass है और लिखित केवल करने के लिए पहले प्रकार ठीक करता है NFA; दूसरा पूरी तरह से खुला छोड़ दिया गया है। कंपाइलर एक ऐसा उदाहरण चुन सकता है जो दायरे में है (जहां अन्य चर Symbol पर मोनोमोर्डेड है), लेकिन यह हमारे डिजाइन सिद्धांत का उल्लंघन करेगा: अब FAState NFA Widget के लिए एक उदाहरण जोड़ना परिणामस्वरूप संदिग्ध कोड, काम करना, compilable गैर-संगत कोड में कोड। तो कंपाइलर रूढ़िवादी रूप से संस्करण में केवल एक उदाहरण के साथ संस्करण को संकलित करने से इंकार कर देता है।

कुछ मानक फिक्स नहीं है:

  1. , एक प्रकार हस्ताक्षर अन्य प्रकार को ठीक करने दें संकलक जो उदाहरण चुनने के लिए कह रही है। दुर्भाग्यवश, यह समाधान आपके लिए काम नहीं करेगा: आपके टाइपक्लास-पॉलिमॉर्फिक मान sId :: FAState a b => a -> Int में इस प्रकार के प्रकार a और b दोनों प्रकार का उल्लेख नहीं है। चूंकि आप इस मूल्य का कभी भी उपयोग नहीं कर सकते हैं, मुझे लगता है कि आधुनिक जीएचसी इस प्रकार के वर्ग को कुछ समय पहले अस्वीकार कर देगा (इससे पहले कि आप किसी भी उदाहरण को लिखें या वर्ग विधियों का आह्वान करने का प्रयास करें), हालांकि मुझे इस समय परीक्षण करने के लिए चारों ओर झूठ बोलने की ज़रूरत नहीं है ।

    बस इस समाधान का एक उदाहरण देने के लिए, sTransitions बजाय sId पर विचार करें: यहाँ प्रकार हस्ताक्षर ताकि आप में गैर-संकलन sTransitions nfa बदल सकते हैं, दोनों चर का उल्लेख करता है हां-संकलन sTransitions nfa :: Transitions Symbol NFA। (अधिक सिद्धांतबद्ध, सामान्यीकृत परिवर्तन केवल विधि पर एक प्रकार का हस्ताक्षर देता है; उदाहरण के लिए, आप आसानी से sTransitions nfa से (sTransitions :: NFA -> Transitions Symbol NFA) dfa तक अनुवाद को सामान्यीकृत कर सकते हैं।)

  2. कार्य निर्भरताओं का उपयोग करें। यहां विचार यह है कि राज्य का प्रकार पूरी तरह से automaton के प्रकार से निर्धारित होता है, इसलिए नैतिक रूप से कक्षा में पहले प्रकार के चर को ठीक करने के लिए यह पर्याप्त होना चाहिए। वाक्य रचना है कि इस तथ्य के बारे में बताता है GHC इस तरह दिखता है:

    class FAState a b | a -> b where 
        {- ...same as before -} 
    -- instance declarations look the same as before, too 
    

    यह दो बातें करता है: पहला, यह GHC बताता है कि अगर यह a जानता है, यह इस का उपयोग एक उदाहरण भले ही यह अभी तक नहीं है चुनने के लिए कर सकते हैं b पता है, और दूसरा, यह जीएचसी को दोबारा जांचने के लिए कहता है कि वर्ग की कोई भी घटना कार्यक्षमता बाधा का उल्लंघन नहीं करती है, यानी कि दो उदाहरणों में a नहीं है लेकिन अलग-अलग b हैं।

  3. परिवारों का उपयोग करें (संबंधित) प्रकार। यह पिछले जैसा ही विचार है, लेकिन शायद एक अधिक परिचित प्रतिमान में व्यक्त किया गया है। इस एक वाक्य विन्यास को इस तरह दिखता है:

    class FAState a where 
        type State a 
        sId :: a -> Int 
        sType :: a -> StateType 
        sTransitions :: a -> Transitions (State a) a 
    
    instance FAState NFA where 
        type State NFA = Symbol 
        -- methods are the same as before 
    

    यह एक नए प्रकार के निर्माता State नामित (जो आप बहुत आगे प्रकार हस्ताक्षर में उपयोग कर सकते हैं) परिचय देता है। आप इस प्रकार के स्तर पर एक फ़ंक्शन के रूप में सोच सकते हैं जो इनपुट प्रकारों के रूप में लेता है जो कक्षा FAState के उदाहरण हैं और उस प्रकार के automaton से जुड़े राज्य के प्रकार को आउटपुट करते हैं।

  4. अपनी घटना घोषणाओं को और अधिक polymorphic बनाओ। यदि जीएचसी शिकायत कर रहा है कि यह दूसरे चर के बारे में पर्याप्त नहीं जानता है, तो ... आप हमेशा यह कह सकते हैं कि दूसरे चर के सभी तत्काल समान हैं (कुछ समानता बाधाओं तक)। उदाहरण के लिए:

    -- class definition same as before 
    instance b ~ Symbol => FAState NFA b where 
        -- methods same as before 
    

    ~ के लिए प्रकार स्तरीय समानता GHC के संकेतन है। जिस तरह से यह काम करता है वह काफी बेवकूफ़ है, और अच्छी तरह से कहीं और वर्णित है (यदि आप वास्तव में उन्हें चाहते हैं तो मैं कुछ लिंक खोद दूंगा), लेकिन स्पष्टीकरण का संक्षिप्त संस्करण यह है कि यह जीएचसी को बताता है कि अगर यह NFA को चुनने के लिए पर्याप्त जानता है पहला चर, तो यह सीधे इस घटना को प्रतिबद्ध कर सकता है, और केवल इसके प्रतिबद्ध होने के बाद यह दोहरी जांच करता है कि दूसरा तर्क वास्तव में Symbol है।

आनंद लें!

+0

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

+0

@ सीए.एमसीकैन आह, हाँ, अच्छा सुझाव। मैं इसे जोड़ दूंगा। –

+0

प्रश्न पोस्ट करने से पहले मैंने फ़ंक्शन निर्भरताओं पर थोड़ा सा पढ़ा था, मुझे नहीं लगता था कि यह इस मामले पर लागू होता है क्योंकि मैं समझ नहीं पा रहा हूं कि संकलक बी के मूल्य को कैसे कम कर सकता है। मेरा मतलब है कि यह कहता है कि "बी का उपयोग करके पता चला जा सकता है", संकलक वास्तव में बी खोजने में मदद करते हैं? – Likhit

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