2012-11-02 14 views
7

टी एल में एक रिकॉर्ड से खेतों को देख के लिए एक डीएसएल निर्माण करने के लिए कैसे; डॉ: मैं पता लगाना कैसे कोड है कि डेटा प्रकार (शायद सिर्फ डबल और बूल) की एक छोटी संख्या में से एक वापस आ जाएगी उत्पन्न करने के लिए मदद की जरूरत है अलग-अलग क्षेत्रों से अलग-अलग रिकॉर्डों पर।हास्केल

लांग प्रपत्र: निम्न डेटा प्रकारों

data Circle = Circle { radius :: Integer, origin :: Point } 
data Square = Square { side :: Integer } 

और कुछ बॉयलरप्लेट कोड

circle = Circle 3 (Point 0 0) 
square = Square 5 

मैं एक छोटे से डीएसएल निर्माण कर रहा हूँ, और यह मानते हुए उपयोगकर्ता की तरह कुछ लिखने होना चाहते हैं निम्नलिखित

circle.origin 
square.side 

और यह जी एनरेट कोड

origin . circle 
side . square 

इसे पार्स करने में, उदाहरण के लिए मेरे पास स्ट्रिंग "सर्कल" और "मूल" होगा। अब मुझे उन्हें फ़ंक्शन कॉल में बदलना होगा। मैं स्पष्ट रूप से इस तरह कुछ कर सकता था:

data Expr a = IntegerE (a -> Integer) 
      | PointE (a -> Point) 

lookupF2I "side" = Just $ IntegerE side 
lookupF2I "radius" = Just $ IntegerE radius 
lookupF2I _  = Nothing 

lookupF2P "origin" = Just $ PointE origin 
lookupF2P _ = Nothing 

और प्रति लौटा डेटा प्रकार में एक लुकअप फ़ंक्शन है। प्रति डेटा प्रकार के एक समारोह को डीएसएल के दृष्टिकोण से व्यावहारिक है कि यह केवल 2 या 3 डेटा प्रकारों से वास्तव में सौदा करेगा। हालांकि, यह मुश्किल से चीजों को करने का एक प्रभावी तरीका लगता है। क्या ऐसा करने का कोई बेहतर तरीका है (निश्चित रूप से)? यदि नहीं, तो क्या कोई तरीका है कि मैं विभिन्न रिकॉर्ड से विभिन्न लुकअप फ़ंक्शंस के लिए कोड जेनरेट कर सकता हूं जिन्हें मैं फ़ील्ड देखने में सक्षम होना चाहता हूं?

दूसरे, वहाँ अभी भी की बात है पार्स "circle" या "square" उचित circle या square फ़ंक्शन को कॉल करने की जरूरत है। हाथ लिखने के लिए होने

instance Lookup Circle where 
    lookupF2I "radius" = Just $ IntegerE radius 
    lookupF2I _  = Nothing 
    lookupF2P "origin" = Just $ PointE origin 
    lookupF2P _  = Nothing 

लेकिन तब है कि मुझे यह पता लगाने की जो प्रकार देखने समारोह, पर लागू करने के लिए होने के साथ छोड़ देता है और बदतर: यदि मैं प्रकार वर्गों का उपयोग इस लागू करने के लिए थे, मैं की तरह कुछ कर सकता है प्रत्येक (कई) रिकॉर्ड के लिए उदाहरण जिन्हें मैं इसका उपयोग करना चाहता हूं।

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

+6

मैं लेंस पैकेज पर दिखेगा: http://hackage.haskell.org/package/lens-3.1 और भी विनाइल https://github.com/jonsterling/Vinyl – ErikR

+1

+1 'lens' के लिए कम से। इसमें टेम्पलेट हास्केल समर्थन है ताकि आप स्वचालित रूप से डेटा प्रकारों से लेंस बना सकें। फिर आपका पार्सिंग कोड स्ट्रिंग्स को उनके संबंधित लेंस में अनुवाद करता है। –

+2

ऐसा लगता है कि आप अपने डीएसएल को कुछ प्रकार के HOAS (उच्च ऑर्डर सार वाक्यविन्यास) के साथ एम्बेड कर रहे हैं जो एम्बेडेड भाषा में मेटा-भाषा (हास्केल) से फ़ंक्शंस का उपयोग करता है। यदि आप HOAS का उपयोग करने पर कुछ नोट्स चाहते हैं तो रॉबर्ट एटकी और सह-लेखकों https://personal.cis.strath.ac.uk/robert.atkey/unembedding.html द्वारा "अनमेडेडिंग" पेपर देखें। यदि होआस का मतलब आपके लिए कुछ भी नहीं है, तो आप शायद सामान्य पहले ऑर्डर सार वाक्यविन्यास के साथ सबसे अच्छे हैं - एक पहचानकर्ता के साथ बिल्टिन फ़ंक्शंस का प्रतिनिधित्व करते हैं और आदिम कार्यों के लिए एक लुकअप वाले वातावरण के साथ मूल्यांकन करते हैं। –

उत्तर

1

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

मुझे लगता है कि लेंस पैकेज ऐसा कर सकता है, लेकिन यह एक आसान और अधिक लचीला समाधान हो सकता है।

यह इस तरह इस्तेमाल किया जा सकता:

import THRecSyntax 
circleOrigin = compDSL "circle.origin.x" 

और इस तरह परिभाषित किया गया है:

{-# LANGUAGE TemplateHaskell #-} 
import Language.Haskell.TH 

compDSL :: String -> Q Exp 
compDSL s = return 
      $ foldr1 AppE 
      $ map (VarE . mkName) 
      (reverse $ splitEvery '.' s) 

तो परिणाम अभिव्यक्ति हो जाएगा: x (origin circle)

नोट: splitEvery एक समारोह है कि दिए गए तत्व को निकालने वाले उपन्यासियों में एक सूची विभाजित करता है। उदाहरण कार्यान्वयन:

splitEvery :: Eq a => a -> [a] -> [[a]] 
splitEvery elem s = splitter (s,[]) 
    where splitter (rest, res) = case elemIndex elem rest of 
      Just dotInd -> let (fst,rest') = splitAt dotInd rest 
          in splitter (tail rest', fst : res) 
      Nothing -> reverse (rest : res) 

यह एक दिग्गज लेकिन प्रकार- सुरक्षित रास्ता दिया वाक्य रचना के साथ एक एम्बेडेड डीएसएल तैयार करना है।

+0

मुझे नहीं पता कि अब तक मैं आपका जवाब कैसे देख रहा हूं। एक लिखने के लिए समय लेने के लिए धन्यवाद :) –