2012-04-23 18 views
11

में वैश्विक झंडे का इलाज करने का सही तरीका मुझे अक्सर एक कोर फ़ंक्शन बनाने की आवश्यकता होती है जिसका उपयोग कई स्थानों पर किसी भी तरह कॉन्फ़िगर करने योग्य होता है - यानी, यह कमांड लाइन स्विच के आधार पर या तो एल्गोरिदम ए या एल्गोरिदम बी का उपयोग कर सकता है; या यदि कोई 'डीबग' ध्वज किसी भी तरह से सेट किया गया है, तो यह stdout को अतिरिक्त-वर्बोज़ जानकारी मुद्रित करता है।हास्केल

मुझे ऐसे वैश्विक ध्वज कैसे लागू करना चाहिए?

मुझे 4 विकल्प दिखाई देते हैं, वे सभी वास्तव में अच्छे नहीं हैं।

1) फ़ंक्शन से कमांड लाइन तर्क पढ़ें - खराब, क्योंकि इसके लिए आईओ मोनड की आवश्यकता है और कोर गणना फ़ंक्शन सभी शुद्ध हैं, मैं वहां आईओ नहीं प्राप्त करना चाहता हूं;

2) मुख्य रूप से 'पत्ते' समारोह के माध्यम से मुख्य/आईओ से पैरामीटर पास करें, जिसे व्यवहार को बदलने की जरूरत है - पूरी तरह से अनुपयोगी, क्योंकि इसका मतलब है कि इस पैरामीटर को पार करने के लिए विभिन्न मॉड्यूल में एक दर्जन असंबद्ध कार्यों को बदलना, और मैं हर बार रैपिंग कोड को बदले बिना कई बार इस तरह के कॉन्फ़िगरेशन विकल्पों को आजमाने की कोशिश करता हूं;

3) एक वास्तविक वैश्विक चर प्राप्त करने के लिए असुरक्षित पैराफॉर्मियो का उपयोग करें - इस तरह के एक साधारण मुद्दे के लिए बदसूरत और ओवरकिल लगता है;

4) फनसीन के बीच में दोनों विकल्पों के लिए कोड है और उनमें से एक टिप्पणी करें। या कार्यों को do_stuff_A और do_stuff_B है, और उनमें से कौन सा एक वैश्विक फ़ंक्शन 'needDebugInfo = True' कहता है, इस पर निर्भर करता है। यही कारण है कि मैं debuginfo अभी कर रहा हूँ, लेकिन यह बदला नहीं जा सकता w/ओ recompile, और यह वास्तव में सबसे अच्छा उपलब्ध रास्ता नहीं होना चाहिए ...

मैं या जरूरत नहीं है वैश्विक परिवर्तनशील चाहते राज्य - मैं एक साधारण वैश्विक झंडा चाहता हूं जो रनटाइम पर अपरिवर्तनीय है लेकिन प्रोग्राम लॉन्च होने पर किसी भी तरह से सेट किया जा सकता है। क्या कोई विकल्प हैं?

उत्तर

3

हमारी नई HFlags पुस्तकालय इस के लिए ठीक है।

आप अपने उदाहरण की तरह एक उदाहरण के उपयोग को देखने के लिए, इस पर विचार करना चाहते हैं:

https://github.com/errge/hflags/blob/master/examples/ImportExample.hs

https://github.com/errge/hflags/blob/master/examples/X/B.hs

https://github.com/errge/hflags/blob/master/examples/X/Y_Y/A.hs

पैरामीटर पासिंग की कोई प्रकार मॉड्यूल के बीच की जरूरत है , और आप एक आसान वाक्यविन्यास के साथ नए झंडे को परिभाषित कर सकते हैं। यह आंतरिक रूप से unsafePerformIO का उपयोग करता है, लेकिन हमें लगता है कि यह एक सुरक्षित तरीके से करता है, और आपको इसके साथ चिंता करने की आवश्यकता नहीं होगी। http://blog.risko.hu/2012/04/ann-hflags-0.html

14

इन दिनों, मैं आवेदन की केवल-पढ़ने वाली स्थिति की संरचना के लिए a Reader monad का उपयोग करना पसंद करता हूं। पर्यावरण स्टार्टअप पर initalized है, और फिर कार्यक्रम के शीर्ष स्तर पर उपलब्ध है।

एक उदाहरण is xmonad:

newtype X a = X (ReaderT XConf (StateT XState IO) a) 
    deriving (Functor, Monad, MonadIO, MonadReader XConf) 

शीर्ष स्तर के कार्यक्रम चलाने की X बजाय IO में भागों; जहां XConf कमांड लाइन फ्लैग (और पर्यावरण चर) द्वारा डेटा संरचना को एकीकृत किया गया है।

XConf राज्य को आवश्यक कार्यों के लिए शुद्ध डेटा के रूप में पारित किया जा सकता है। नए प्रकार के व्यय के साथ आप राज्य तक पहुंचने के लिए सभी MonadReader कोड का पुन: उपयोग भी कर सकते हैं।

यह दृष्टिकोण 2 की अर्थपूर्ण शुद्धता को बरकरार रखता है लेकिन आपको लिखने के लिए कम कोड देता है, क्योंकि मोनड नलसाजी करता है।

मुझे लगता है कि यह "सत्य" हास्केल तरीका केवल-पढ़ने के लिए कॉन्फ़िगरेशन स्थिति करने का तरीका है।

-

दृष्टिकोण है कि unsafePerformIO का उपयोग वैश्विक राज्य प्रारंभ करने में भी निश्चित रूप से काम करते हैं, है, लेकिन (जैसे जब आप अपने कार्यक्रम समवर्ती या समानांतर बनाने के) अंततः आप काट लिए जाते हैं। उनके पास funny initialization semantics भी है।

9

आप Reader मोनैड का उपयोग कर सकते हैं ताकि हर जगह चारों ओर एक पैरामीटर गुजरने के समान प्रभाव प्राप्त हो सके।आवेदक शैली सामान्य कार्यात्मक कोड की तुलना में ओवरहेड काफी कम कर सकती है, लेकिन यह अभी भी बहुत अजीब हो सकती है। यह कॉन्फ़िगरेशन समस्या का सबसे आम समाधान है, लेकिन मुझे यह बहुत संतोषजनक नहीं लगता है; वास्तव में, स्पष्ट रूप से चारों ओर पैरामीटर गुजरना अक्सर बदसूरत है।

एक विकल्प reflection पैकेज है, जो आपको टाइपक्लास संदर्भों के माध्यम से इस तरह के सामान्य कॉन्फ़िगरेशन डेटा को पार करने देता है, जिसका अर्थ है कि आपके किसी भी कोड को अतिरिक्त मूल्य, केवल प्रकारों को प्लंब करने के लिए बदलना नहीं है। असल में, आप अपने प्रोग्राम में प्रत्येक इनपुट/परिणाम प्रकार में एक नया प्रकार पैरामीटर जोड़ते हैं, ताकि किसी निश्चित कॉन्फ़िगरेशन के संदर्भ में चल रहे सब कुछ उस प्रकार के कॉन्फ़िगरेशन के अनुरूप हो। उस प्रकार से आप कई विन्यासों का उपयोग करके गलती से मूल्यों को मिश्रित कर देते हैं, और आपको रनटाइम पर संबंधित कॉन्फ़िगरेशन तक पहुंच प्रदान करता है।

यह आवेदक शैली में सब कुछ लिखने के ऊपरी हिस्से से बचाता है, जबकि अभी भी सुरक्षित है, और आपको कई विन्यासों को मिश्रण करने की इजाजत देता है। यह लगता है की तुलना में यह बहुत आसान है; यहां an example है।

(पूर्ण discloure:। मैं प्रतिबिंब पैकेज पर काम किया है)

2

एक अन्य विकल्प GHC implicit parameters है:

वहाँ पर इस सामग्री के बारे में एक ब्लॉग पोस्ट है। ये आपके विकल्प का एक कम दर्दनाक संस्करण देते हैं (2): मध्यवर्ती प्रकार के हस्ताक्षर संक्रमित हो जाते हैं, लेकिन आपको किसी भी मध्यवर्ती कोड को बदलने की आवश्यकता नहीं है।

यहाँ एक उदाहरण है:

{-# LANGUAGE ImplicitParams #-} 
import System.Environment (getArgs)  

-- Put the flags in a record so you can add new flags later 
-- without affecting existing type signatures. 
data Flags = Flags { flag :: Bool } 

-- Leaf functions that read the flags need the implicit argument 
-- constraint '(?flags::Flags)'. This is reasonable. 
leafFunction :: (?flags::Flags) => String 
leafFunction = if flag ?flags then "do_stuff_A" else "do_stuff_B" 

-- Implicit argument constraints are propagated to callers, so 
-- intermediate functions also need the implicit argument 
-- constraint. This is annoying. 
intermediateFunction :: (?flags::Flags) => String 
intermediateFunction = "We are going to " ++ leafFunction 

-- Implicit arguments can be bound at the top level, say after 
-- parsing command line arguments or a configuration file. 
main :: IO() 
main = do 
    -- Read the flag value from the command line. 
    commandLineFlag <- (read . head) `fmap` getArgs 
    -- Bind the implicit argument. 
    let ?flags = Flags { flag = commandLineFlag } 
    -- Subsequent code has access to the bound implicit. 
    print intermediateFunction 

यह We are going to do_stuff_A प्रिंट आप तर्क True के साथ इस कार्यक्रम चलाते हैं; तर्क False के साथ यह We are going to do_stuff_B प्रिंट करता है।

मुझे लगता है कि यह दृष्टिकोण the reflection package mentioned in another answer जैसा है, और मुझे लगता है कि HFlags mentioned in the accepted answer शायद एक बेहतर विकल्प है, लेकिन मैं पूर्णता के लिए यह उत्तर जोड़ रहा हूं।