2012-07-10 12 views
5

के लिए आवेदक कन्स्ट्रक्टर रिकॉर्ड के लिए एक पार्सर बनाने के लिए मैं सामान्य रूप से हैकेल रिकॉर्ड के लिए आवेदक रचनाकार बनाना चाहता हूं।रिकॉर्ड्स

data Record = Record {i :: Int, f :: Float} 

निर्माता मैं चाहता हूँ: बुनियादी प्रकार के लिए

Record <$> pInt <*> pFloat 

Parsers दिए गए हैं:

class Parseable a where 
    getParser :: Parser a 

instance Parseable Int where 
    getParser = pInt 

instance Parseable Float where 
    getParser = pFloat 

वहाँ किसी भी पुस्तकालयों कि पहले से ही ऐसा कर सकते हैं कर रहे हैं

रिकॉर्ड पर विचार करें ? क्या रिकॉर्ड के लिए GetParser को परिभाषित करना संभव हो सकता है? अग्रिम में धन्यवाद।

+0

बस स्पष्ट करने के लिए: यदि आप 'Parseable Record' के लिए एक उदाहरण चाहते हैं आप के लिए उत्पन्न करने की? – kosmikus

उत्तर

9

उदाहरण के लिए, regular लाइब्रेरी का उपयोग करके किया जा सकता है।

{-# LANGUAGE FlexibleContexts  #-} 
{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE TypeFamilies   #-} 
{-# LANGUAGE TypeOperators  #-} 
{-# LANGUAGE UndecidableInstances #-} 

import Control.Applicative 
import Generics.Regular 

कम से कम सबसे लोकप्रिय पार्सर Combinator लाइब्रेरी के दो एक अनुप्रयोगी-functor इंटरफेस के साथ आते हैं:: इस लाइब्रेरी के साथ कार्य करना आम तौर पर कुछ भाषा एक्सटेंशन की आवश्यकता है उदाहरण के लिए देखते हैं,, uu-parsinglib और parsec, लेकिन चीजों को सरल बनाने के लिए आइए यहां सरल सूची-सफल सफलता पार्सर्स का उपयोग करें।

newtype Parser a = Parser {runParser :: ReadS a} 

instance Functor Parser where 
    fmap f p = Parser $ \s -> [(f x, s') | (x, s') <- runParser p s] 

instance Applicative Parser where 
    pure x = Parser $ \s -> [(x, s)] 
    p <*> q = Parser $ \s -> 
    [(f x, s'') | (f, s') <- runParser p s, (x, s'') <- runParser q s'] 

instance Alternative Parser where 
    empty = Parser $ \_ -> [] 
    p <|> q = Parser $ \s -> runParser p s ++ runParser q s 

(। कि नोट type ReadS a = String -> [(a, String)])

pSym :: Char -> Parser Char 
pSym c = Parser $ \s -> case s of 
    (c' : s') | c == c' -> [(c', s')] 
    _     -> [] 

pInt :: Parser Int 
pInt = Parser reads 

pFloat :: Parser Float 
pFloat = Parser reads 

सीधी, हमने:

class Parseable a where 
    getParser :: Parser a 

instance Parseable Int where 
    getParser = pInt 

instance Parseable Float where 
    getParser = pFloat 

और, अपने रिकॉर्ड प्रकार के लिए, के रूप में वांछित:

data Record = Record {i :: Int, f :: Float} 

instance Parseable Record where 
    getParser = Record <$> pInt <* pSym ' ' <*> pFloat 

अब , हम सामान्य रूप से कैसे करते हैं ऐसे पार्सर उत्पन्न करें? तब

type instance PF Record = K Int :*: K Float 

, हम Record प्रकार वर्ग Regular का एक उदाहरण बनाने:

सबसे पहले, हम Record के तथाकथित पैटर्न functor (विवरण के लिए regular के दस्तावेज़ देखें) को परिभाषित

instance Regular Record where 
    from (Record n r) = K n :*: K r 
    to (K n :*: K r) = Record n r 

class ParseableF f where 
    getParserF :: Parser a -> Parser (f a) 

instance ParseableF (K Int) where 
    getParserF _ = K <$> pInt 

instance ParseableF (K Float) where 
    getParserF _ = K <$> pFloat 

instance (ParseableF f, ParseableF g) => ParseableF (f :*: g) where 
    getParserF p = (:*:) <$> getParserF p <* pSym ' ' <*> getParserF p 
:

इसके बाद, हम एक सामान्य पार्सर को परिभाषित

अब (सभी नियमित प्रकारों को कवर करने के लिए, आप कुछ और उदाहरणों प्रदान करना होगा, लेकिन इन आपके उदाहरण के लिए कर सकते हैं।)

, हम प्रदर्शन कर सकते हैं कि वर्ग Regular में हर प्रकार के (दिए गए के लिए एक ParseableF उदाहरण इसका पैटर्न फ़ैक्टर) एक पार्सर के साथ आता है:

instance (Regular a, ParseableF (PF a)) => Parseable a where 
    getParser = to <$> getParserF getParser 

चलिए इसे स्पिन के लिए लेते हैं। Parseable (यानी Int, Float, और निश्चित रूप से Record) के मूल उदाहरण ड्रॉप करें और केवल एक सामान्य उदाहरण रखें।ये हम चले:

> runParser (getParser :: Parser Record) "42 3.14" 
[(Record {i = 42, f = 3.14},"")] 

नोट: यह कैसे नियमित रूप से पुस्तकालय का उपयोग कर सामान्य पारसर्स प्राप्त करने के लिए का सिर्फ एक बहुत ही बुनियादी उदाहरण है। लाइब्रेरी स्वयं generic list-of-successes parser के साथ आता है जो रिकॉर्ड के साथ विशेष रूप से अच्छी चीजें करता है। आप इसे पहले बाहर देखना चाह सकते हैं। इसके अलावा, लाइब्रेरी टेम्पलेट हास्केल समर्थन के साथ आता है ताकि Regular के उदाहरण स्वचालित रूप से प्राप्त किए जा सकें। इन उदाहरणों में रिकॉर्ड लेबल के लिए विशेष संरचना प्रकार शामिल हैं, ताकि आप अपने सामान्य कार्य रिकॉर्ड प्रकारों को वास्तव में फैंसी का इलाज कर सकें। दस्तावेज़ देखें।

3

जितना मैं regular पैकेज की तरह, मैं करते रहे कि ghc-7.2 के बाद से GHC है सामान्य प्रतिनिधित्व प्रकार पाने, ताकि आप खाका हास्केल पर भरोसा करने के ऐसा करने की जरूरत नहीं है के लिए निर्मित समर्थन चाहते हैं।

डीब्लेलिक्स द्वारा सुझाए गए समाधान की तुलना में परिवर्तन निम्न हैं। आप थोड़ा अलग झंडे और मॉड्यूल आयातित की जरूरत है:

{-# LANGUAGE DeriveGeneriC#-} 
{-# LANGUAGE DefaultSignatures #-} 
{-# LANGUAGE FlexibleContexts #-} 
{-# LANGUAGE TypeOperators #-} 

import Control.Applicative 
import GHC.Generics 

तुम अब भी ऊपर के रूप में Parser और उसके उदाहरणों को परिभाषित। आप अपने Record प्रकार के लिए वर्ग Generic प्राप्त करने के लिए की जरूरत है:

data Record = Record { i :: Int, f :: Float } 
    deriving (Generic, Show) 

वर्ग Generic बहुत वर्ग Regular के समान है। आपको PF या अब Regular का उदाहरण परिभाषित करने की आवश्यकता नहीं है।

के बजाय ParseableF, हम एक वर्ग Parseable' शैली में बहुत मिलती-जुलती कोई परिभाषित करते हैं, अभी तक कभी तो थोड़ा अलग:

class Parseable a where 
    getParser :: Parser a 
    default getParser :: (Generic a, Parseable' (Rep a)) => Parser a 
    getParser = to <$> getParser' 

instance Parseable Int where 
    getParser = pInt 

instance Parseable Float where 
    getParser = pFloat 
:

class Parseable' f where 
    getParser' :: Parser (f a) 

-- covers base types such as Int and Float: 
instance Parseable a => Parseable' (K1 m a) where 
    getParser' = K1 <$> getParser 

-- covers types with a sequence of fields (record types): 
instance (Parseable' f, Parseable' g) => Parseable' (f :*: g) where 
    getParser' = (:*:) <$> getParser' <* pSym ' ' <*> getParser' 

-- ignores meta-information such as constructor names or field labels: 
instance Parseable' f => Parseable' (M1 m l f) where 
    getParser' = M1 <$> getParser' 

अंत में, Parseable के लिए, हम एक सामान्य डिफ़ॉल्ट विधि को परिभाषित

अब, Record टाइप पैरासेबल एक खाली उदाहरण घोषणा प्रदान करने जितना आसान है:

instance Parseable Record 

उदाहरण के रूप में पहले से काम करता है:

> runParser (getParser :: Parser Record) "42 3.14" 
[(Record {i = 42, f = 3.14},"")]