2011-06-26 17 views
11

क्या कुछ जीएचसी एक्सटेंशन के साथ अपने डेटा प्रकार के साथ एक फ़ंक्शन अनुकरण करना संभव है? मैं क्या करना चाहता हूं उदा।क्या आपके अपने डेटा प्रकार का उपयोग करके एक फ़ंक्शन अनुकरण करना संभव है?

(काल्पनिक वाक्य रचना)

data MyFunc = MyFunc String (Int->Int) 

instance (Int->Int) MyFunc where 
    ($) (MyFunc _ f) i = f i 

inc = MyFunc "increment" (1+) 

test = inc 1

अर्थात डेटा जिसमें इसके साथ कुछ मेटा-सूचना होती है और पैटर्न मिलान किया जा सकता है, लेकिन जिसे अभी भी नियमित फ़ंक्शन की तरह कहा जा सकता है। अब, मुझे पता है कि मैं अपने स्वयं के इंफिक्स ऑपरेटर को $$ जैसे परिभाषित कर सकता हूं और inc $$ 1 पर कॉल कर सकता हूं, लेकिन नियमित फ़ंक्शन कॉल सिंटैक्स का उपयोग करने में सक्षम होने से एम्बेडेड डीएसएल में बहुत उपयोगी होगा।

उत्तर

18

हां, यह सीमित सीमा तक किया जा सकता है।

लेकिन पहले हम

{-# LANGUAGE Rank2Types #-} 

तो मैं अच्छे शो समर्थन प्राप्त कर सकते हैं के

data M a b = M { name :: Int -> String -> String, eval :: a -> b } 

मैं अपने नाम करने के लिए और अधिक संरचना जोड़ रहा परिभाषित करते हैं की आवश्यकता होगी। अब

class Magic m where 
    magic :: M a b -> m a b 

instance Magic M where 
    magic = id 

instance Magic (->) where 
    magic (M _ f) = f 

, प्रकार पर विचार करें::

type MyFunc a b = forall m. Magic m => m a b 

magic का परिणाम प्रकार या तो (a -> b) या एक M a b है;)

फिर एक वर्ग को परिभाषित करने देता है।

तो इसका उपयोग MyFunc के सदस्य के रूप में किया जा सकता है। अब, इस प्रकार कुछ हद तक नाकाफी है, क्योंकि आप उदाहरणों उस पर प्रेषण नहीं कर सकते हैं, लेकिन इसका मतलब यह है कि

inc :: MyFunc Int Int 
inc = magic (M (const (showString "inc")) (+1)) 

test :: Int 
test = inc 1 

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

हम उन्हें दिखाने का एक अच्छा तरीका भी बना सकते हैं। भले ही हम MyFunc पर शो का उपयोग नहीं कर सकते हैं, हम इसे M के लिए परिभाषित कर सकते हैं।

instance Show (M a b) where 
    showsPrec d (M s _) = s d 

फिर हम एक समारोह हम M a b (और विस्तार के किसी भी MyFunc द्वारा) एक M a b बाहर निकलने के लिए करने के लिए आवेदन कर सकते हैं कर सकते हैं।

m :: M a b -> M a b 
m = id 

और हम MyFunc रों को दिखाने के लिए एक विशेष Combinator परिभाषित कर सकते हैं:

showM :: MyFunc a b -> String 
showM f = show (m f) 

फिर हम खेल सकते हैं। हम MyFunc एस की रचनाओं को परिभाषित कर सकते हैं।

infixr 9 .# 
(.#) :: MyFunc b c -> MyFunc a b -> MyFunc a c 
f .# g = magic (M 
    (\d -> showParen (d > 9) $ showsPrec 10 (m f) . 
           showString " . " . 
           showsPrec 9 (m g)) 
    (f . g)) 

inc2 :: MyFunc Int Int 
inc2 = inc .# inc 

test2 :: Int 
test2 = inc2 1 

bar, baz :: String 
bar = showM inc 
baz = showM inc2 

और क्योंकि मैं नाम करने के लिए पर्याप्त संरचना दे दी है, हम और भी जटिल रचनाओं के लिए सही parenthesization, अनावश्यक कोष्ठकों के बिना मिलता है।

*Main> showM $ inc2 .# inc 
"(inc . inc) . inc" 

*Main> showM $ inc .# inc2 
"inc . inc . inc" 

लेकिन याद रखें, आप, MyFunc के लिए किसी भी इंस्टेंस को परिभाषित करने में सक्षम नहीं होगा, क्योंकि यह केवल एक type, और नहीं एक newtype हो सकता है। उदाहरणों को परिभाषित करने के लिए आपको उन्हें M पर परिभाषित करना होगा, और फिर उस प्रकार को बदलने के लिए m का उपयोग करें ताकि अंतर्निहित प्रेषण को पकड़ने के लिए एक प्रकार हो।

रैंक 2 प्रकार की वजह से, यदि आप स्थानीय संदर्भों में इनका भारी उपयोग करते हैं, तो आप NoMonoLocalBinds और/या NoMonomorphismRestriction चालू करना भी चाह सकते हैं।

+9

यह भयानक है। मुझे यह पसंद है। –

5

नहीं, वाक्यविन्यास f e ओवरलोड नहीं किया जा सकता है। f में S -> T टाइप होना चाहिए।

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

3

आप फ़ंक्शन कॉल सिंटैक्स को सीधे अधिभारित नहीं कर सकते हैं, नहीं।

आप अपना खुद का कस्टम तीर प्रकार बना सकते हैं --- Control.Arrow देखें। फिर आप arrow notation का उपयोग करके अपने "फ़ंक्शंस" का आह्वान कर सकते हैं (आपको अपनी स्रोत फ़ाइल के शीर्ष पर {-# LANGUAGE Arrows #-} शामिल करना होगा)। चाहे यह आपके लिए पर्याप्त है, आपके डीएसएल की जरूरतों पर निर्भर करता है।

+0

तीर के लिए ++ 1। – fuz

+0

आवेदकों के बारे में मत भूलना! उनके पास एक एप्लिकेशन ऑपरेटर भी है। – luqui

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