2014-09-08 10 views
7

शुभ दिन हर कोई।अंतिम टैगलेस दृष्टिकोण में डीएसएल शब्द को फिर से परिभाषित करने के लिए कैसे?

हमारा एप्लिकेशन कुछ व्यावसायिक तर्क का वर्णन करने के लिए टाइप किए गए डीएसएल का उपयोग करता है। डीएसएल कई टैगलेस दुभाषियों के साथ आता है।

यहाँ कैसे अपनी शर्तों की घोषणा की जाती है:

{-# LANGUAGE TypeFamilies #-} 
{-# LANGUAGE EmptyDataDecls #-} 

class Ctl impl where 
    -- Lift constants. 
    cnst :: Show t => t -> impl t 
    -- Obtain the state. 
    state :: impl (Maybe Int) 

    -- Test for equality. 
    eq :: impl Int -> impl Int -> impl Bool 
    -- If-then-else. 
    ite :: impl Bool -> impl t -> impl t -> impl t 

    -- Processing outcomes. 
    retry :: impl Outcome 
    finish :: impl Outcome 

    -- Require a value. 
    req :: impl (Maybe t) -> impl t 

व्यापार तर्क तो यह डीएसएल में कोड का हिस्सा का उपयोग कर वर्णित है:

proc1 :: Ctl impl => impl Outcome 
proc1 = ite (req state `eq` cnst 5) finish retry 

इन उच्च स्तरीय परिभाषाओं दुभाषिए के साथ उपयोग करने के लिए रखा जाता है। मैं कैसे व्यवसाय प्रक्रियाओं परिभाषित कर रहे हैं की एक पठनीय शाब्दिक वर्णन प्राप्त करने के लिए एक पाठ दुभाषिया है:

λ> (evalText proc1) :: String 
"If (My current state)* equals 5, then Finish, else Retry processing" 

इस तरह के विवरण के लिए एक संदर्भ के रूप में इस्तेमाल किया जाता है:

newtype TextE t = TextE { evalText :: String } 

instance Ctl TextE where 
    cnst v = TextE $ show v 
    state = TextE "My current state" 
    eq v1 v2 = TextE $ concat [evalText v1, " equals ", evalText v2] 
    ite cond t e = 
    TextE $ 
    concat ["If ", evalText cond, ", then ", evalText t, ", else ", evalText e] 
    retry = TextE "Retry processing" 
    finish = TextE "Finish" 
    req v = TextE $ concat ["(", evalText v, ")*"] 

texte साथ डीएसएल व्याख्या एक स्ट्रिंग का उत्पादन उपयोगकर्ताओं/विश्लेषकों के लिए।

newtype HaskellE t = HaskellE { evalHaskell :: HaskellType t } 

-- Interface between types of DSL and Haskell. 
type family HaskellType t 

instance Ctl HaskellE where 
    cnst v = HaskellE v 
    state = HaskellE dummyState 
    eq v1 v2 = HaskellE $ evalHaskell v1 == evalHaskell v2 
    ite cond t e = 
    HaskellE $ 
    if (evalHaskell cond) 
    then (evalHaskell t) 
    else (evalHaskell e) 
    retry = HaskellE $ print "Retrying..." 
    finish = HaskellE $ print "Done!" 
    req [email protected](HaskellE v) = 
    case v of 
     Just v' -> HaskellE v' 
     Nothing -> 
     HaskellE (error $ 
        "Could not obtain required value from ") -- ++ evalText term) 

-- Dummy implementations so that this post may be evaluated 
dummyState = Just 5 
type Outcome = IO() 
type instance HaskellType t = t 

यह दुभाषिया का उत्पादन runnable हास्केल कोड:

मैं भी मेटा-भाषा एक और दुभाषिया के साथ (हास्केल) है, जो कैसे आवेदन वास्तव में नियमों का पालन करती है करने के लिए एक डीएसएल अवधि मूल्यांकन कर सकते हैं

λ> (evalHaskell proc1) :: IO() 
"Done!" 

मेरी समस्या के लिए अब: मैं HaskellE दुभाषिया से texte दुभाषिया उपयोग करना चाहते हैं। उदाहरण के लिए, मैं req की विफल शाखा को परिभाषित करना चाहता हूं जिसमें त्रुटि संदेश में नेस्टेड शब्द (आमतौर पर evalText term द्वारा प्राप्त) का टेक्स्ट प्रतिनिधित्व शामिल है। उपरोक्त HaskellE के लिए req कार्यान्वयन में प्रासंगिक कोड पर टिप्पणी की गई है। टिप्पणी को वापस लाया गया है, तो कोड की तरह

HaskellE (error $ 
       "Could not obtain required value from " ++ evalText term) 

लग रहा है हालांकि, प्रकार प्रणाली मुझे ऐसा करने से रोकता है:

tagless.lhs:90:71: Couldn't match expected type ‘TextE t0’ … 
       with actual type ‘HaskellE (Maybe t)’ 
    Relevant bindings include 
     v :: HaskellType (Maybe t) 
     (bound at /home/dzhus/projects/hs-archive/tagless.lhs:85:22) 
     term :: HaskellE (Maybe t) 
     (bound at /home/dzhus/projects/hs-archive/tagless.lhs:85:7) 
     req :: HaskellE (Maybe t) -> HaskellE t 
     (bound at /home/dzhus/projects/hs-archive/tagless.lhs:85:3) 
    In the first argument of ‘evalText’, namely ‘term’ 
    In the second argument of ‘(++)’, namely ‘evalText term’ 
Compilation failed. 

संदेश मूल रूप से कहा गया है कि दुभाषिया HaskellE पहले से ही चुना गया है जब प्रकार परिवर्तनीय impl तत्काल था, और मैं अंदर HaskellE से TextE दुभाषिया का उपयोग नहीं कर सकता।

मैं अपने सिर को चारों ओर क्यों नहीं प्राप्त कर सकता हूं: मैं शब्द को हास्केल से टेक्स्टई में दोबारा कैसे दोहरा सकता हूं?

अगर मैं यहाँ पूरी तरह से गलत हूँ, मैं अपने दृष्टिकोण नयी आकृति प्रदान करना ताकि मैं वास्तव में यह HaskellE अंदर फिर से लागू करने के बिना हास्केल एक से पाठ दुभाषिया का उपयोग कर सकते हैं? ऐसा लगता है कि यह अंतिम संभव के बजाय प्रारंभिक दृष्टिकोण के साथ काफी व्यवहार्य है।

मैंने अपने वास्तविक डीएसएल को तोड़ दिया है और संक्षेप के लिए को सरलीकृत किया है।

+1

(क्या है "का उत्पादन बर्ताव करता है टैगलेस "संदर्भ में?) – user2864740

+2

@ user2864740 यह पेपर बताता है कि इसका क्या अर्थ है; यह चर्चा करता है कि अनुभाग 3.1 में कौन से टैग हैं: http://okmij.org/ftp/tagless-final/course/lecture.pdf –

+0

@DavidYoung धन्यवाद! – user2864740

उत्तर

7

आप मूल्य बनाने वाले अभिव्यक्ति के बारे में मूल्य और जानकारी दोनों का ट्रैक रख सकते हैं। यदि आप ऐसा करते हैं तो आप अपने अंतिम टैगलेस प्रतिनिधित्व के कुछ प्रदर्शन लाभ खो देंगे।

data Traced t a = Traced {evalTraced :: HaskellType a, trace :: t a} 

हम एक TextE का पता लगाने के साथ इसका इस्तेमाल करने की उम्मीद करते हैं, तो हम सुविधा के लिए निम्नलिखित को परिभाषित करेंगे

evalTextTraced :: Traced TextE a -> HaskellType a 
evalTextTraced = evalTraced 

इस वर्ग के लिए हमें एक trace

class Show1 f where 
    show1 :: f a -> String 

instance Show1 TextE where 
    show1 = evalText 

instance (Show1 t) => Show1 (Traced t) where 
    show1 = show1 . trace 
से त्रुटि संदेश ठीक करने के लिए अनुमति देता है

यह दुभाषिया किसी अन्य Ctl t दुभाषिया का पता लगाता है कि हमकी व्याख्या करते समय त्रुटि संदेशों को पुनर्प्राप्त कर सकते हैं।

instance (Show1 t, Ctl t) => Ctl (Traced t) where 
    cnst v = Traced v (cnst v) 
    state = Traced dummyState state 
    eq (Traced v1 t1) (Traced v2 t2) = Traced (v1 == v2) (eq t1 t2) 
    ite (Traced vc tc) (Traced vt tt) (Traced ve te) = Traced (if vc then vt else ve) (ite tc tt te) 
    retry = Traced (print "Retrying...") retry 
    finish = Traced (print "Done!") finish 
    req (Traced v t) = 
     case v of 
      Just v' -> Traced v' rt 
      Nothing -> Traced (error ("Could not obtain required value from " ++ show1 rt)) rt 
     where rt = req t 

आपका उदाहरण के रूप में उम्मीद

print . evalText . trace $ proc1 
evalTextTraced proc1 

"If (My current state)* equals 5, then Finish, else Retry processing" 
"Done!" 

हम कर सकते हैं अभी भी evalText एक असफल आवश्यकता के साथ एक उदाहरण है, लेकिन इसे चलाने के लिए कोशिश कर रहा एक जानकारीपूर्ण त्रुटि संदेश

proc2 :: Ctl impl => impl Outcome 
proc2 = ite (req (cnst Nothing) `eq` cnst 5) finish retry 

print . evalText . trace $ proc2 
evalTextTraced proc2 

"If (Nothing)* equals 5, then Finish, else Retry processing" 
finaltagless.hs: Could not obtain required value from (Nothing)* 
संबंधित मुद्दे