2011-07-03 13 views
27

मैं का उपयोग करें और सी # (dllimport), की तरह से उच्च क्रम प्रकार हस्ताक्षरों के साथ हास्केल कार्यों कॉल कर सकते हैं कैसे ...का प्रयोग सी # में उच्च क्रम हास्केल प्रकार

double :: (Int -> Int) -> Int -> Int -- higher order function 

typeClassFunc :: ... -> Maybe Int -- type classes 

data MyData = Foo | Bar    -- user data type 
dataFunc :: ... -> MyData 

क्या सी # में इसी प्रकार के हस्ताक्षर हैं ?

[DllImport ("libHSDLLTest")] 
private static extern ??? foo(???); 

इसके अतिरिक्त (क्योंकि यह आसान हो सकता है): मैं "अज्ञात" सी # के भीतर हास्केल प्रकार कैसे उपयोग कर सकते हैं, तो मैं कम से कम उनके आसपास किसी भी विशेष प्रकार जानते हुए भी पारित कर सकते हैं, बिना सी #? मुझे सबसे महत्वपूर्ण कार्यक्षमता की आवश्यकता है, जो कि एक प्रकार की कक्षा (जैसे मोनैड या तीर) को पास करना है।

मुझे पहले से ही how to compile a Haskell library to DLL पता है और सी # के भीतर उपयोग करें, लेकिन केवल प्रथम क्रम के कार्यों के लिए उपयोग करें। मुझे Stackoverflow - Call a Haskell function in .NET, Why isn't GHC available for .NET और hs-dotnet के बारे में भी पता है, जहां मुझे कोई भी दस्तावेज और नमूने नहीं मिला (सी # से हास्केल दिशा के लिए)।

उत्तर

18

मैं यहां FUZxxl की पोस्ट पर अपनी टिप्पणी पर विस्तृत जानकारी दूंगा।
आपके द्वारा पोस्ट किए गए उदाहरण FFI का उपयोग कर सभी संभव हैं। एक बार जब आप एफएफआई का उपयोग करके अपने कार्यों को निर्यात कर लेते हैं तो आप पहले से ही प्रोग्राम को डीएलएल में संकलित कर सकते हैं।

.NET को सी, सी ++, COM, आदि के साथ आसानी से इंटरफ़ेस करने में सक्षम होने के इरादे से डिज़ाइन किया गया था। इसका अर्थ यह है कि एक बार जब आप अपने कार्यों को एक डीएलएल में संकलित करने में सक्षम होते हैं, तो आप इसे (अपेक्षाकृत) आसान कह सकते हैं .NET से जैसा कि मैंने आपके द्वारा पोस्ट की गई दूसरी पोस्ट में पहले उल्लेख किया है, ध्यान रखें कि आपके कार्यों को निर्यात करते समय आप कौन सा कॉलिंग कॉन्फ़्रेंस निर्दिष्ट करते हैं। .NET में मानक stdcall है, जबकि ccall का उपयोग करते हुए हास्केल FFI निर्यात के अधिकांश (उदाहरण) उदाहरण हैं।

अब तक केवल एकमात्र सीमा जिसे मैंने एफएफआई द्वारा निर्यात किया जा सकता है, polymorphic types है, या ऐसे प्रकार जो पूरी तरह लागू नहीं हैं। जैसे दयालु * के अलावा कुछ भी (आप Maybe निर्यात नहीं कर सकते हैं लेकिन उदाहरण के लिए आप Maybe Int निर्यात कर सकते हैं)।

मैंने एक उपकरण Hs2lib लिखा है जो आपके उदाहरण में आपके किसी भी कार्य को स्वचालित रूप से कवर और निर्यात करेगा। इसमें unsafe सी # कोड उत्पन्न करने का विकल्प भी है जो इसे बहुत अधिक "प्लग और प्ले" बनाता है। असुरक्षित कोड चुनने का कारण यह है कि पॉइंटर्स को संभालना आसान है, जिससे बदले में डेटास्ट्रक्चर के लिए मार्शलिंग करना आसान हो जाता है।

पूरा होने के लिए मैं विस्तार से बताऊंगा कि टूल आपके उदाहरणों को कैसे प्रबंधित करता है और मैं पॉलिमॉर्फिक प्रकारों को संभालने की योजना कैसे बना सकता हूं।

  • उच्च आदेश कार्यों

जब उच्च आदेश कार्यों के निर्यात, समारोह हल्के से बदल दिया जाना चाहिए। उच्च-आदेश तर्कों को FunPtr के तत्व बनने की आवश्यकता है। असल में उन्हें स्पष्ट फ़ंक्शन पॉइंटर्स (या सी # में प्रतिनिधियों) के रूप में माना जाता है, जो कि अनिवार्य भाषाओं में आमतौर पर उच्च आदेश दिया जाता है।
मान लिया जाये कि हम CInt में Int परिवर्तित डबल के प्रकार

FunPtr (CInt -> CInt) -> CInt -> IO CInt 

इन प्रकार के एक आवरण समारोह (doubleA इस मामले में) के लिए उत्पन्न कर रहे हैं जो double के बजाय निर्यात किया जाता है में

(Int -> Int) -> Int -> Int 

से तब्दील हो जाता है अपने आप। रैपर निर्यातित मानों और मूल फ़ंक्शन के लिए अपेक्षित इनपुट मानों के बीच मानचित्र बनाता है। आईओ की आवश्यकता है क्योंकि FunPtr का निर्माण एक शुद्ध संचालन नहीं है।
याद रखने की एक बात यह है कि FunPtr का निर्माण या हटाना करने का एकमात्र तरीका स्थिर रूप से आयात बना रहा है जो जीएचसी को इसके लिए स्टब्स बनाने का निर्देश देता है।

foreign import stdcall "wrapper" mkFunPtr :: (Cint -> CInt) -> IO (FunPtr (CInt -> CInt)) 
foreign import stdcall "dynamic" dynFunPtr :: FunPtr (CInt -> CInt) -> CInt -> CInt 

"आवरण" समारोह हमें बनाने के लिए एक FunPtr और "डायनामिक"FunPtr एक के बाद एक सम्मान करने की अनुमति देता है।

सी # में हम एक IntPtr के रूप में इनपुट की घोषणा और उसके बाद एक समारोह सूचक है कि हम फोन कर सकते हैं, या एक समारोह सूचक से एक IntPtr बनाने के लिए उलटा समारोह बनाने के लिए Marshaller सहायक समारोह Marshal.GetDelegateForFunctionPointer का उपयोग करें।

यह भी याद रखें कि फ़ंक्शन के कॉलिंग सम्मेलन को FunPtr के लिए तर्क के रूप में पारित किया जाना चाहिए, उस कार्य के कॉलिंग सम्मेलन से मेल खाना चाहिए जिस पर तर्क पारित किया जा रहा है। दूसरे शब्दों में, &foo से bar को foo और bar को उसी कॉलिंग सम्मेलन के लिए पास करने की आवश्यकता है।

  • उपयोगकर्ता डेटाटाइप्स

एक उपयोगकर्ता डेटाप्रकार निर्यात वास्तव में काफी सीधे आगे है। प्रत्येक डेटाटाइप के लिए जिसे Storable निर्यात करने की आवश्यकता है, इस प्रकार के लिए उदाहरण बनाना होगा। इस उदाहरण में इस प्रकार के निर्यात/आयात करने में सक्षम होने के लिए जीएचसी की मार्शलिंग जानकारी निर्दिष्ट होती है। अन्य चीजों के अलावा आपको size और alignment प्रकार को परिभाषित करने की आवश्यकता होगी, साथ ही किसी सूचक को प्रकार के मानों को पढ़ने/लिखने के तरीके के साथ। मैं इस कार्य के लिए आंशिक रूप से Hsc2hs का उपयोग करता हूं (इसलिए फ़ाइल में सी मैक्रोज़)।

newtypes या datatypes बस एक कन्स्ट्रक्टर आसान है। ये एक सपाट संरचना बन जाते हैं क्योंकि इन प्रकारों का निर्माण/विनाश करते समय केवल एक ही संभावित विकल्प होता है। एकाधिक कन्स्ट्रक्टर वाले प्रकार एक संघ बन जाते हैं (Layout विशेषता वाला एक स्ट्रक्चर Explicit पर सी #) पर सेट है। हालांकि हमें यह जानने के लिए एक enum शामिल करने की आवश्यकता है कि किस निर्माण का उपयोग किया जा रहा है।

सामान्य रूप में

, डेटाप्रकार Single रूप

data Single = Single { sint :: Int 
         , schar :: Char 
         } 

परिभाषित बनाता Storable उदाहरण

instance Storable Single where 
    sizeOf _ = 8 
    alignment _ = #alignment Single_t 

    poke ptr (Single a1 a2) = do 
     a1x <- toNative a1 :: IO CInt 
     (#poke Single_t, sint) ptr a1x 
     a2x <- toNative a2 :: IO CWchar 
     (#poke Single_t, schar) ptr a2x 

    peek ptr = do 
     a1' <- (#peek Single_t, sint) ptr :: IO CInt 
     a2' <- (#peek Single_t, schar) ptr :: IO CWchar 
     x1 <- fromNative a1' :: IO Int 
     x2 <- fromNative a2' :: IO Char 
     return $ Single x1 x2 

और सी struct

typedef struct Single Single_t; 

struct Single { 
    int sint; 
    wchar_t schar; 
} ; 

समारोह foo :: Int -> Singlefoo :: CInt -> Ptr Single के रूप में निर्यात किया जाएगा निम्नलिखित जबकि कई निर्माता के साथ एक डेटाप्रकार

data Multi = Demi { mints :: [Int] 
        , mstring :: String 
        } 
      | Semi { semi :: [Single] 
        } 

निम्नलिखित सी कोड उत्पन्न करता है:

enum ListMulti {cMultiDemi, cMultiSemi}; 

typedef struct Multi Multi_t; 
typedef struct Demi Demi_t; 
typedef struct Semi Semi_t; 

struct Multi { 
    enum ListMulti tag; 
    union MultiUnion* elt; 
} ; 

struct Demi { 
    int* mints; 
    int mints_Size; 
    wchar_t* mstring; 
} ; 

struct Semi { 
    Single_t** semi; 
    int semi_Size; 
} ; 

union MultiUnion { 
    struct Demi var_Demi; 
    struct Semi var_Semi; 
} ; 

Storable उदाहरण अपेक्षाकृत सीधे आगे है और सी struct परिभाषा से आसान पालन करना चाहिए।

  • एप्लाइड प्रकार

मेरे निर्भरता ट्रेसर के लिए प्रकार Maybe Int दोनों प्रकार Int और Maybe पर निर्भरता के लिए के लिए फेंकना होगा। इसका मतलब यह है, कि जब Maybe Int के लिए Storable उदाहरण पैदा सिर की तरह

instance Storable Int => Storable (Maybe Int) where 

यही है, वहाँ के रूप में आवेदन प्रकार खुद भी निर्यात किया जा सकता के तर्कों के लिए एक Storable उदाहरण है aslong लग रहा है।

चूंकि Maybe a को पॉलीमॉर्फिक तर्क Just a के रूप में परिभाषित किया गया है, तो structs बनाते समय, कुछ प्रकार की जानकारी खो जाती है। Structs में void* तर्क होगा, जिसे आपको मैन्युअल रूप से सही प्रकार में परिवर्तित करना होगा। विकल्प मेरी राय में बहुत बोझिल था, जो विशेष संरचनाओं को भी बनाना था। जैसे संरचना शायद INT। लेकिन सामान्य मॉड्यूल से उत्पन्न विशेष संरचनाओं की मात्रा जल्दी से इस तरह से विस्फोट कर सकती है। (इसे बाद में ध्वज के रूप में जोड़ सकते हैं)।

जानकारी के इस नुकसान को कम करने के लिए मेरा उपकरण किसी भी Haddock फ़ंक्शन के लिए मिले दस्तावेज को जेनरेट की गई टिप्पणियों के रूप में निर्यात करेगा। यह टिप्पणी में मूल हास्केल प्रकार हस्ताक्षर भी रखेगा। एक आईडीई तब इन्हें इंटेलिसेंस (कोड कॉम्प्लेशन) के हिस्से के रूप में प्रस्तुत करेगा।

इन उदाहरणों के साथ है मैं चीजों की नेट पक्ष के लिए कोड ommited है, तो आप आप सिर्फ Hs2lib के उत्पादन में देख सकते हैं कि में रुचि रखते हैं।

कुछ अन्य प्रकार हैं जिन्हें विशेष उपचार की आवश्यकता है। विशेष रूप से Lists और Tuples

  1. सूचियों को सरणी के आकार को पारित करने की आवश्यकता है, जिससे हम मार्शल से हैं, क्योंकि हम अप्रबंधित भाषाओं के साथ इंटरफेस कर रहे हैं जहां सरणी का आकार स्पष्ट रूप से ज्ञात नहीं है। इसके विपरीत जब हम एक सूची वापस करते हैं, तो हमें सूची के आकार को वापस करने की भी आवश्यकता होती है।
  2. टुपल्स प्रकारों में विशेष निर्माण होते हैं, उन्हें निर्यात करने के लिए, हमें पहले उन्हें "सामान्य" डेटाटाइप पर मैप करना होगा, और उनको निर्यात करना होगा। उपकरण में यह 8-टुपल्स तक किया जाता है।

    • बहुरूपी प्रकार

समस्या बहुरूपी प्रकार e.g. map :: (a -> b) -> [a] -> [b] साथ कि a की size और b पता नहीं कर रहे हैं। यही है, तर्कों और वापसी मूल्य के लिए जगह आरक्षित करने का कोई तरीका नहीं है क्योंकि हम नहीं जानते कि वे क्या हैं। मैं आपको a और b के लिए संभावित मान निर्दिष्ट करने और इन प्रकारों के लिए विशेष रैपर फ़ंक्शन बनाने की अनुमति देकर इसका समर्थन करने की योजना बना रहा हूं। दूसरे आकार पर, अनिवार्य भाषा में मैं उपयोगकर्ता को चुने गए प्रकारों को प्रस्तुत करने के लिए overloading का उपयोग करूंगा।

कक्षाओं के लिए, हास्केल की खुली दुनिया धारणा आमतौर पर एक समस्या होती है (उदाहरण के लिए एक उदाहरण किसी भी समय जोड़ा जा सकता है)। हालांकि संकलन के समय केवल उदाहरणों की एक सांख्यिकीय रूप से ज्ञात सूची उपलब्ध है। मैं एक विकल्प पेश करना चाहता हूं जो इन सूचीओं का उपयोग करके जितना संभव हो उतना विशेष उदाहरण निर्यात करेगा। जैसे निर्यात (+) संकलन समय पर सभी ज्ञात Num उदाहरणों के लिए एक विशेष कार्य निर्यात करता है (उदा। Int, Double, आदि)।

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

मुझे आशा है कि इससे मदद मिलेगी, और मुझे उम्मीद है कि यह बहुत लंबा नहीं था।

अद्यतन: कुछ हद तक एक बड़ा गॉचाचा है जिसे मैंने हाल ही में खोजा है। हमें याद रखना होगा कि .NET में स्ट्रिंग प्रकार अपरिवर्तनीय है। तो जब मार्शलर इसे हास्केल कोड से बाहर भेजता है, तो सीडब्लूस्ट्रिंग हम मूल की एक प्रति प्राप्त करते हैं। हम में इसे मुक्त करने के लिए है। जब सी # में जीसी किया जाता है तो यह सीडब्लूस्ट्रिंग को प्रभावित नहीं करेगा, जो एक प्रति है।

समस्या यह है कि जब हम इसे हास्केल कोड में मुक्त करते हैं तो हम freeCWString का उपयोग नहीं कर सकते हैं। सूचक को सी (msvcrt.dll) के आवंटन के साथ आवंटित नहीं किया गया था। इसे हल करने के तीन तरीके हैं (जिन्हें मैं जानता हूं)।

  • हास्केल फ़ंक्शन को कॉल करते समय स्ट्रिंग के बजाय अपने सी # कोड में char * का उपयोग करें। जब आप रिटर्न कॉल करते हैं तो आपके पास पॉइंटर मुक्त होता है, या fixed का उपयोग करके फ़ंक्शन प्रारंभ करना होता है।
  • आयात CoTaskMemFree हास्केल में आयात करें और हास्केल
  • में सूचक को स्ट्रिंग के बजाय स्ट्रिंगबिल्डर का उपयोग करें। मैं इस बारे में पूरी तरह से निश्चित नहीं हूं, लेकिन विचार यह है कि चूंकि स्ट्रिंगबिल्डर को मूल सूचक के रूप में लागू किया जाता है, इसलिए मार्शलर इस सूचक को आपके हास्केल कोड (जो इसे बीटीडब्ल्यू भी अपडेट कर सकता है) में पास करता है। जब कॉल रिटर्न के बाद जीसी किया जाता है, तो स्ट्रिंगबिल्डर को मुक्त किया जाना चाहिए।
+0

मुझे लगता है कि काफी कुछ सब कुछ शामिल है। मैं तुमसे प्यार करता हूँ दोस्तों/gals! :) –

+0

आपका स्वागत है :) यदि आपके पास कोई और प्रश्न पूछने के लिए स्वतंत्र महसूस है :) – Phyx

4

क्या आपने FFI के माध्यम से कार्यों को निर्यात करने का प्रयास किया था? यह आपको कार्यों में एक और सी-आईएसएच इंटरफ़ेस बनाने की अनुमति देता है। मुझे संदेह है कि सीधे H # से Haskell फ़ंक्शंस को कॉल करना संभव है। अधिक जानकारी के लिए दस्तावेज़ देखें। (उपरोक्त लिंक)।

कुछ परीक्षण करने के बाद, मुझे लगता है कि आम तौर पर, एफएफआई के माध्यम से टाइप-पैरामीटर के साथ उच्च आदेश कार्यों और कार्यों को निर्यात करना संभव नहीं है। [उद्धरण वांछित]

+0

यह निश्चित रूप से संभव है क्योंकि - जैसा कि मैंने पोस्ट किया है - जैसा कि मैंने पहले ही किया था, लेकिन केवल प्रथम क्रम के कार्यों के साथ। और सभी ट्यूटोरियल और उदाहरण भी प्रथम-आदेश कार्यों तक ही सीमित हैं। –

+0

@ लैंबडोर मैंने आपके प्रश्न को गलत समझा। जिस ब्लॉग पोस्ट से आप लिंक करते हैं वह पहले से ही विदेशी निर्यात निर्माण का उपयोग करता है।यदि आप मेरे द्वारा प्रदान किए गए लिंक को देखते हैं, तो आप देखेंगे कि जीएचसी उन निर्यातित कार्यों के लिए प्रोटोटाइप युक्त एक स्टब '.c'-फ़ाइल उत्पन्न करता है। आप उच्च-प्रयोग प्रकारों के साथ कुछ प्रयोगों को आजमा सकते हैं और कर सकते हैं। – fuz

+0

@lambdor कुछ परीक्षणों के बाद, ऐसा लगता है कि एफएफआई केवल "सरल" प्रकारों की अनुमति देता है। यह है: कोई प्रकार चर नहीं, कोई उच्च आदेश फ़ंक्शन नहीं, कोई एडीटी नहीं। – fuz

3

ठीक है, FUZxxl के लिए धन्यवाद, एक समाधान वह "अज्ञात प्रकार" के लिए आया था। आईओ संदर्भ के भीतर डेटा को हास्केल एमवीआर में स्टोर करें और सी # से हास्केल से पहले ऑर्डर फ़ंक्शंस के साथ संवाद करें। यह कम से कम सरल परिस्थितियों के लिए समाधान हो सकता है।

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