2012-04-05 8 views
10

this response से another question में, थोड़ा हास्केल कोड स्केच दिया गया था जो कमांड लाइन तर्कों पर वाक्यविन्यास जांच करने के लिए कुछ कोड को कारक करने के लिए रैपर फ़ंक्शंस का उपयोग करता है।मैं पैटर्न मिलान करने वाले कार्यों के लिए बॉयलरप्लेट कोड लिखने से कैसे बच सकता हूं?

takesSingleArg :: (String -> IO()) -> [String] -> IO() 
takesSingleArg act [arg] = act arg 
takesSingleArg _ _  = showUsageMessage 

takesTwoArgs :: (String -> String -> IO()) -> [String] -> IO() 
takesTwoArgs act [arg1, arg2] = act arg1 arg2 
takesTwoArgs _ _   = showUsageMessage 

एक तरह से (शायद Template Haskell का उपयोग कर?) तर्कों की प्रत्येक संख्या के लिए अतिरिक्त कार्यों लिखने के लिए होने से बचाने के वहाँ है: यहाँ कोड है जो मैं सरल करने के लिए कोशिश कर रहा हूँ का हिस्सा है? आदर्श रूप में, मैं की तरह कुछ (मैं इस वाक्य रचना ऊपर बना रहा हूं)

generateArgumentWrapper<2, showUsageMessage> 

लिखने में सक्षम होना चाहते हैं और वह

\fn args -> case args of 
       [a, b] -> fn a b 
       _  -> showUsageMessage 

आदर्श रूप में करने के लिए फैलता है, मैं भी के परिवर्तनशील हो सकता था generateArgumentWrapper मेटा-कार्य करने के लिए तर्क, ताकि मैं

generateArgumentWrapper<2, asInt, asFilePath, showUsageMessage> 

कर सकता है और वह

के लिए विस्तारित

क्या कोई इसे प्राप्त करने के तरीके से अवगत है? मनमानी कार्यों के लिए कमांड लाइन तर्क ([String]) को बांधना वास्तव में एक आसान तरीका होगा। या शायद एक पूरी तरह से अलग, बेहतर दृष्टिकोण है?

उत्तर

12

हास्केल polyvariadic कार्य करती है। कल्पना कीजिए कि आप क्या आप

runAct (Run f) x = f x 
runAct (Res _) x = error "wrong function type" 

takeNargs' 0 (Res b) _ = b 
takeNargs' 0 (Run _) _ = error "wrong function type" 
takeNargs' n act (x:xs) = takeNargs' (n-1) (runAct act x) xs 
takeNargs' _ _ [] = error "not long enough list" 

अब चाहता हूं

data Act = Run (String -> Act) | Res (IO()) 

कुछ कार्यों के साथ की तरह एक प्रकार था, तुम सब आप की जरूरत इस Act प्रकार में कार्यों मार्शल करने के लिए है। आप कुछ एक्सटेंशन

{-# LANGUAGE FlexibleInstances, FlexibleContexts #-} 

जरूरत है और फिर आप

class Actable a where 
    makeAct :: a -> Act 
    numberOfArgs :: a -> Int 

instance Actable (String -> IO()) where 
    makeAct f = Run $ Res . f 
    numberOfArgs _ = 1 

instance Actable (b -> c) => Actable (String -> (b -> c)) where 
    makeAct f = Run $ makeAct . f 
    numberOfArgs f = 1 + numberOfArgs (f "") 

अब परिभाषित कर सकते हैं आप

takeNArgs n act = takeNargs' n (makeAct act) 

जो यह आसान अपने मूल कार्यों को परिभाषित करने के लिए बनाता है परिभाषित कर सकते हैं

takesSingleArg :: (String -> IO()) -> [String] -> IO() 
takesSingleArg = takeNArgs 1 

takesTwoArgs :: (String -> String -> IO()) -> [String] -> IO() 
takesTwoArgs = takeNArgs 2 

लेकिन हम भी कर सकते हैं बेहतर

takeTheRightNumArgs f = takeNArgs (numberOfArgs f) f 

आश्चर्यजनक रूप से, यह काम करता है (GHCi)

*Main> takeTheRightNumArgs putStrLn ["hello","world"] 
hello 
*Main> takeTheRightNumArgs (\x y -> putStrLn x >> putStrLn y) ["hello","world"] 
hello 
world 

संपादित करें: उपरोक्त कोड और अधिक जटिल की तुलना में यह करने की जरूरत है।वास्तव में, आप चाहते हैं कि

class TakeArgs a where 
    takeArgs :: a -> [String] -> IO() 

instance TakeArgs (IO()) where 
    takeArgs a _ = a 

instance TakeArgs a => TakeArgs (String -> a) where 
    takeArgs f (x:xs) = takeArgs (f x) xs 
    takeArgs f [] = error "end of list" 
+0

मानक लाइब्रेरी में टेक्स्ट.प्रिंटफ भी देखें, जो वही काम करता है, कम या ज्यादा। ध्यान दें कि तर्कों की गलत संख्या प्रदान करना एक रनटाइम त्रुटि है, एक प्रकार त्रुटि नहीं। –

1

कॉम्बिनेटर्स आपका मित्र हैं। इस प्रयास करें:

take1 :: [String] -> Maybe String 
take1 [x] = Just x 
take1 _ = Nothing 

take2 :: [String] -> Maybe (String,String) 
take2 [x,y] = Just (x,y) 
take2 _ = Nothing 

take3 :: [String] -> Maybe ((String,String),String) 
take3 [x,y,z] = Just ((x,y),z) 
take3 _ = Nothing 

type ErrorMsg = String 

with1 :: (String -> IO()) -> ErrorMsg -> [String] -> IO() 
with1 f msg = maybe (fail msg) f . take1 

with2 :: (String -> String -> IO()) -> ErrorMsg -> [String] -> IO() 
with2 f msg = maybe (fail msg) (uncurry f) . take2 

with3 :: (String -> String -> String -> IO()) -> ErrorMsg -> [String] -> IO() 
with3 f msg = maybe (fail msg) (uncurry . uncurry $ f) . take3 

foo a b c = putStrLn $ a ++ " :: " ++ b ++ " = " ++ c 

bar = with3 foo "You must send foo a name, type, definition" 

main = do 
    bar [ "xs", "[Int]", "[1..3]" ] 
    bar [ "xs", "[Int]", "[1..3]", "What am I doing here?" ] 

और अगर आप भाषा एक्सटेंशन जबर्दस्ती पसंद:

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, UndecidableInstances #-} 

foo a b c = putStrLn $ a ++ " :: " ++ b ++ " = " ++ c 
foo_msg = "You must send foo a name, type, definition" 

class ApplyArg a b | a -> b where 
    appArg :: ErrorMsg -> a -> [String] -> IO b 

instance ApplyArg (IO b) b where 
    appArg _msg todo [] = todo 
    appArg msg _todo _ = fail msg 

instance ApplyArg v q => ApplyArg (String -> v) q where 
    appArg msg todo (x:xs) = appArg msg (todo x) xs 
    appArg msg _todo _ = fail msg 

quux :: [String] -> IO() 
quux xs = appArg foo_msg foo xs 

main = do 
    quux [ "xs", "[int]", "[1..3]" ] 
    quux [ "xs", "[int]", "[1..3]", "what am i doing here?" ] 
2

आप कमांड लाइन तर्कों से निपटने के लिए मौजूदा पुस्तकालयों का उपयोग करना चाहेंगे। मेरा मानना ​​है कि डी-फैक्टो मानक अभी cmdargs है, लेकिन अन्य विकल्प मौजूद हैं, जैसे कि ReadArgs और console-program

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

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