2012-05-10 17 views
23

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

अन्य मामलों को देखना बहुत अच्छा होगा जहां unsafePerformIO का उपयोग करने के लिए स्वीकार्य है।

उत्तर

20

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

उदाहरण के लिए से अधिकतर कार्यों को unsafePerformIO के साथ बुलाया जा सकता है।

आप सही हैं कि unsafePerformIO और पॉइंटर्स आमतौर पर मिश्रण नहीं करते हैं। उदाहरण के लिए, आप

p_sin(double *p) { return sin(*p); } 

है भले ही आप सिर्फ एक सूचक से एक मूल्य पढ़ रहे हैं लगता है, यह unsafePerformIO उपयोग करने के लिए सुरक्षित नहीं है। यदि आप p_sin लपेटते हैं, तो एकाधिक कॉल पॉइंटर तर्क का उपयोग कर सकते हैं, लेकिन अलग-अलग परिणाम प्राप्त कर सकते हैं। यह सुनिश्चित करने के लिए कि यह सूचक अपडेट के संबंध में ठीक से अनुक्रमित है, यह सुनिश्चित करने के लिए फ़ंक्शन को IO में रखना आवश्यक है।

यह उदाहरण स्पष्ट एक कारण बनाना चाहिए क्यों यह असुरक्षित है:

# file export.c 

#include <math.h> 
double p_sin(double *p) { return sin(*p); } 

# file main.hs 
{-# LANGUAGE ForeignFunctionInterface #-} 

import Foreign.Ptr 
import Foreign.Marshal.Alloc 
import Foreign.Storable 

foreign import ccall "p_sin" 
    p_sin :: Ptr Double -> Double 

foreign import ccall "p_sin" 
    safeSin :: Ptr Double -> IO Double 

main :: IO() 
main = do 
    p <- malloc 
    let sin1 = p_sin p 
     sin2 = safeSin p 
    poke p 0 
    putStrLn $ "unsafe: " ++ show sin1 
    sin2 >>= \x -> putStrLn $ "safe: " ++ show x 

    poke p 1 
    putStrLn $ "unsafe: " ++ show sin1 
    sin2 >>= \x -> putStrLn $ "safe: " ++ show x 

जब संकलित, इस कार्यक्रम आउटपुट

$ ./main 
unsafe: 0.0 
safe: 0.0 
unsafe: 0.0 
safe: 0.8414709848078965 

हालांकि सूचक द्वारा संदर्भित मूल्य दो संदर्भों के बीच बदल गया है "sin1" के लिए, अभिव्यक्ति का फिर से मूल्यांकन नहीं किया जाता है, जिससे बालों का उपयोग किया जा रहा है। चूंकि safeSin (और इसलिए sin2) आईओ में है, इसलिए प्रोग्राम को अभिव्यक्ति का पुनर्मूल्यांकन करने के लिए मजबूर किया जाता है, इसलिए अपडेट किए गए पॉइंटर डेटा का उपयोग इसके बजाय किया जाता है।

+0

क्या आप विस्तारित कर सकते हैं कि पॉइंटर्स के साथ आपके उदाहरण में 'असुरक्षितफॉर्मियो' का उपयोग करना असुरक्षित क्यों है? –

+0

@VladtheImpala - मैंने उम्मीदपूर्वक इसे और अधिक स्पष्ट रूप से संबोधित करने के लिए संपादित किया है। –

+1

'unsafePerformIO' का उल्लेख आपके उत्तर में स्पष्ट रूप से नहीं किया गया है। क्या होगा यदि मैं 'unsafePerformIO' में' poke pz >> safeSin p' में अनुक्रमों को अनुक्रमित करता हूं: 'mySin z = unsafePerformIO (poke pz >> safeSin p)' और फिर 'mySin' को सामान्य फ़ंक्शन के रूप में उपयोग करें, मुझे चाहिए ठीक है, है ना? –

23

यहां सी को शामिल करने की आवश्यकता नहीं है। unsafePerformIO समारोह, किसी भी स्थिति जहां में इस्तेमाल किया जा सकता

  1. तुम्हें पता है कि इसके उपयोग सुरक्षित है, और

  2. आप अपनी सुरक्षा हास्केल प्रकार प्रणाली का उपयोग कर साबित करने में असमर्थ रहे हैं।

    memoize :: Ord a => (a -> b) -> a -> b 
    memoize f = unsafePerformIO $ do 
        memo <- newMVar $ Map.empty 
        return $ \x -> unsafePerformIO $ modifyMVar memo $ \memov -> 
         return $ case Map.lookup x memov of 
          Just y -> (memov, y) 
          Nothing -> let y = f x 
             in (Map.insert x y memov, y) 
    

    (यह मेरे सिर के ऊपर से है, इसलिए मैं कोई अंदाज़ा नहीं है यदि कोड बॉक्स में खुला त्रुटियाँ हैं:

उदाहरण के लिए, यदि आप एक memoize समारोह unsafePerformIO का उपयोग कर बना सकते हैं।)

memoize फ़ंक्शन का उपयोग करता है और एक Memoization शब्दकोश को संशोधित करता है, लेकिन एक पूरी के रूप में कार्य के बाद से सुरक्षित है, तो आप इसे (IO इकाई का कोई उपयोग नहीं के साथ एक शुद्ध प्रकार) दे सकते हैं। हालांकि, आपको ऐसा करने के लिए unsafePerformIO का उपयोग करना होगा।

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

फ़ुटनोट 2: अक्सर कोड unsafePerformIO का उपयोग करता है में वास्तव में सूक्ष्म कीड़े हैं, उदाहरण के लिए सिर्फ एक संभव उपयोग की एक स्केच है। विशेष रूप से, unsafePerformIO अनुकूलक के साथ खराब तरीके से बातचीत कर सकते हैं।

+1

यह भी देखें: [डेटा.मेमोउग्ली] (http://hackage.haskell.org/packages/archive/uglymemo/0.1.0.1/doc/html/src/Data-MemoUgly.html#memo) –

+1

मुझे लगता है कि यह उत्तर अगर आप यह निर्दिष्ट करना चाहते थे कि प्रोग्रामर जानता है कि 'असुरक्षितफॉर्मियो' सुरक्षित है तो यह बहुत मजबूत होगा। –

+1

@ जॉन: अगर हमारे पास उस प्रश्न का सटीक उत्तर था, तो हम एक आदर्श प्रकार प्रणाली बनाने में सक्षम होंगे। सामान्य रूप से समस्या अव्यवस्थित है। –

12

स्पष्ट रूप से यदि इसका कभी भी उपयोग नहीं किया जाना चाहिए, तो यह मानक पुस्तकालयों में नहीं होगा। ;-)

आप इसका उपयोग क्यों कर सकते हैं इसके कई कारण हैं। उदाहरणों में शामिल हैं:

  • वैश्विक परिवर्तनीय स्थिति आरंभ करना। (चाहे आप कभी ऐसी पहली जगह में एक बात होनी चाहिए एक पूरी अन्य चर्चा है ...)

  • लेज़ी आई/ओ इस चाल का उपयोग कर कार्यान्वित किया जाता है। (फिर, आलसी I/O पहली जगह में एक अच्छा विचार है बहस योग्य है।)

  • trace फ़ंक्शन इसका उपयोग करता है। (फिर भी, यह trace दिखाता है कि आप कल्पना कर सकते हैं उससे कम उपयोगी है।)

  • शायद सबसे महत्वपूर्ण बात यह है कि आप डेटा संरचनाओं को लागू करने के लिए इसका उपयोग कर सकते हैं जो संदर्भित पारदर्शी हैं, लेकिन आंतरिक कोड को अशुद्ध कोड का उपयोग करके कार्यान्वित किया जाता है। अक्सर ST मोनड आपको ऐसा करने देगा, लेकिन कभी-कभी आपको थोड़ा unsafePerformIO की आवश्यकता होती है।

आलसी I/O अंतिम बिंदु के विशेष मामले के रूप में देखा जा सकता है। तो यादें कर सकते हैं।

उदाहरण के लिए, एक "अपरिवर्तनीय", बढ़ने योग्य सरणी पर विचार करें। आंतरिक रूप से आप इसे एक शुद्ध "हैंडल" के रूप में कार्यान्वित कर सकते हैं जो परिवर्तनीय सरणी को इंगित करता है। हैंडल में सरणी के उपयोगकर्ता-दृश्य आकार होते हैं, लेकिन वास्तविक अंतर्निहित म्यूटेबल सरणी उस से बड़ी है। जब उपयोगकर्ता सरणी में "संलग्न" होता है, तो एक नया, बड़े आकार के साथ एक नया हैंडल वापस कर दिया जाता है, लेकिन अंतर्निहित म्यूटेबल सरणी को म्यूट करके एपेंड किया जाता है।

आप ST मोनैड के साथ ऐसा नहीं कर सकते हैं। (या बल्कि, आप कर सकते हैं, लेकिन इसे अभी भी unsafePerformIO की आवश्यकता है।)

ध्यान दें कि इस तरह की चीज़ को सही तरीके से प्राप्त करना मुश्किल है। और यदि आप गलत हैं तो टाइप चेकर नहीं पकड़ेगा। (unsafePerformIO क्या करता है; यह टाइप चेकर को यह जांच नहीं करता है कि आप इसे सही तरीके से कर रहे हैं!) उदाहरण के लिए, यदि आप "पुराने" हैंडल में जोड़ते हैं, तो सही काम करने के लिए अंतर्निहित म्यूटेबल सरणी की प्रतिलिपि बनाना होगा। इसे भूल जाओ, और आपका कोड बहुत अजीब व्यवहार करेगा।

अब, अपने वास्तविक प्रश्न का उत्तर देने के लिए: कोई विशेष कारण नहीं है कि "कुछ भी पॉइंटर्स" unsafePerformIO के लिए नो-नो होना चाहिए। यह पूछते समय कि इस फ़ंक्शन का उपयोग करना है या नहीं, केवल महत्व का सवाल यह है: क्या अंत उपयोगकर्ता किसी भी दुष्प्रभाव को ऐसा करने से देख सकता है?

यदि यह केवल एक चीज है जो कुछ बफर बनाता है तो उपयोगकर्ता शुद्ध कोड से "देख" नहीं सकता है, यह ठीक है। अगर यह डिस्क पर एक फाइल को लिखता है ... इतना अच्छा नहीं है।

एचटीएच।

+2

आलसी I/O को 'unsafePerformIO' के साथ लागू नहीं किया गया है, यह' unsafeInterleaveIO', एक काफी अलग (और कम असुरक्षित) फ़ंक्शन का उपयोग करता है। –

+0

@JohnL 'unsafeInterleaveIO' के लिए प्रलेखन टेक्स्ट की 1 पंक्ति है। यह शानदार होगा यदि आप एक संदर्भ प्रदान कर सकते हैं जो बताता है कि यह फ़ंक्शन _actually_ क्या करता है ... – MathematicalOrchid

+2

विकी पर "आईओ इनसाइड" पृष्ठ और मैग्नस थ्रिंग द्वारा ब्लॉग पोस्ट देखें। वे शायद सबसे अच्छे संसाधन हैं (हालांकि अभी भी काफी कम है, मैं मानता हूं)। http://www.haskell.org/haskellwiki/IO_inside http://therning.org/magnus/archives/249 –

3

तरह से मैं इसे देख, विभिन्न unsafe* nonfunctions वास्तव में केवल मामलों में इस्तेमाल किया जाना चाहिए, जहां आप कुछ है कि संदर्भित पारदर्शिता लेकिन जिनके कार्यान्वयन अन्यथा एक नया आदिम क्षमता जोड़ने के लिए संकलक या क्रम प्रणाली बढ़ाने की आवश्यकता होगी का सम्मान करता है करना चाहते हैं। यह आसान, अधिक मॉड्यूलर, पठनीय, रखरखाव करने योग्य और असुरक्षित सामानों का उपयोग करने के लिए चुस्त है, इस तरह की चीजों के लिए भाषा कार्यान्वयन को संशोधित करना है।

एफएफआई काम अक्सर आंतरिक रूप से आपको इस तरह की चीज करने की आवश्यकता होती है।

5

हास्केल में वैश्विक परिवर्तनशील चर का दृष्टांत को मानक चाल:

{-# NOINLINE bla #-} 
bla :: IORef Int 
bla = unsafePerformIO (newIORef 10) 

मैं भी इसका इस्तेमाल वैश्विक चर से अधिक बंद करने के लिए करता है, तो मुझे लगता है मैं प्रदान कार्यों के बाहर यह करने के लिए उपयोग को रोकने के लिए चाहते हैं:

{-# NOINLINE printJob #-} 
printJob :: String -> Bool -> IO() 
printJob = unsafePerformIO $ do 
    p <- newEmptyMVar 
    return $ \a b -> do 
       -- here's the function code doing something 
       -- with variable p, no one else can access. 
+1

मैंने कुछ 'असली' कोड (dbignore OSX टूल/https://github.com/tkonolige/dbignore/blob/master/ignore.hs) में असुरक्षित PerformIO क्या कर रहा है की खोज की है और लगता है कि यह वास्तव में इसके लिए उपयोग किया जाता है। – OderWat

+0

हम में से कुछ इस मुहावरे [बुराई और अशिष्ट] (http://www.catb.org/jargon/html/E/evil-and-rude.html) पर विचार करते हैं। – dfeuer

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

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