2016-08-29 9 views
7

मैं एमएल-स्टाइल मॉड्यूल की गहरी समझ की दिशा में काम कर रहा हूं: मुझे लगता है कि अवधारणा महत्वपूर्ण है और मुझे प्रोत्साहित करने की सोच पसंद है। मैं सिर्फ हूं जो अब पैरामीटर की जांच कर रहा है जो पैरामीट्रिक प्रकारों और पैरामीट्रिक मॉड्यूल के बीच उत्पन्न हो सकता है। मैं इस मामले के बारे में सोचने के लिए टूल की तलाश कर रहा हूं जो मेरे प्रोग्राम बनाने के दौरान स्मार्ट डिज़ाइन निर्णय लेने में मेरी सहायता करेगा।मॉड्यूल डिज़ाइन करते समय टाइप-लेवल या मॉड्यूल-स्तर पर पैरामीटर करने का निर्णय कैसे लें?

मुट्ठी मैं सामान्य रूप से अपने प्रश्न का वर्णन करने की कोशिश करूंगा। फिर मैं एक सीखने वाली परियोजना से ठोस उदाहरण प्रदान करूंगा जिस पर मैं काम कर रहा हूं। अंत में, मैं एक बिंदु पर आकर्षित करने के लिए सामान्य प्रश्न पर फिर से विचार करूंगा।

(। मुझे खेद है कि मैं अभी तक अधिक संक्षेप इस सवाल खड़ा करने के लिए पर्याप्त नहीं जानता हूँ)

सामान्य शब्दों में, तनाव मुझे पता चला है यह है: कार्यों सबसे लचीला, और खुले हैं व्यापक पुन: उपयोग के लिए, जब हम उन्हें पैरामीट्रिक प्रकार हस्ताक्षर (जहां उचित हो) प्रदान करते हैं। हालांकि, मॉड्यूल सबसे लचीले और को सबसे बड़े पुन: उपयोग के लिए खोलते हैं जब हम मॉड्यूल के अंदर फ़ंक्शंस के पैरामीटर को सील करते हैं, और इसके बजाय पूरे मॉड्यूल को किसी दिए गए प्रकार पर पैरामीटरकृत करते हैं।

इस अंतर का एक तैयार उदाहरण मॉड्यूल उन है कि ORD_SET लागू साथ LIST हस्ताक्षर लागू की तुलना में पाया जा सकता। एक मॉड्यूल List:LIST उपयोगी कार्यों का एक गुच्छा प्रदान करता है, किसी भी प्रकार पर पैरामीटरकृत। एक बार जब हम List मॉड्यूल को परिभाषित या लोड कर लेते हैं, तो हम किसी भी प्रकार की सूची की जांच, कुशलतापूर्वक, या बनाने के लिए प्रदान किए जाने वाले किसी भी फ़ंक्शन को आसानी से लागू कर सकते हैं। दूसरी ओर

val strList = [email protected] (["a","b"], ["c","d"]) 
val intList = [email protected] ([1,2,3,4], [5,6,7,8]) 

, हम साथ आदेश दिया सौदा करना चाहते हैं: उदाहरण के लिए, अगर हम दोनों तार और पूर्णांकों के साथ काम कर रहे हैं, हम एक और एक ही मॉड्यूल दोनों प्रकार के निर्माण और मूल्यों में हेरफेर करने के लिए उपयोग कर सकते सेट, मामले अलग हैं: आदेशित सेटों की आवश्यकता है कि ऑर्डरिंग रिलेशन उनके सभी तत्वों, पर होल्ड करें और कोई भी ठोस कंक्रीट फ़ंक्शन compare : 'a * 'a -> order प्रत्येक प्रकार के संबंध के साथ उत्पादन नहीं कर सकता है। नतीजतन, हमें आदेशित सेटों में प्रत्येक प्रकार के लिए ORD_SET हस्ताक्षर को संतुष्ट करने के लिए एक अलग मॉड्यूल की आवश्यकता होती है।इस प्रकार, आदेश का निर्माण या तार और पूर्णांकों का आदेश दिया सेट में हेरफेर करने में, हम विभिन्न मॉड्यूल प्रत्येक प्रकार [1] के लिए लागू करना चाहिए:

structure IntOrdSet = BinarySetFn (type ord_key = int 
            val compare = Int.compare) 
structure StrOrdSet = BinarySetFn (type ord_key = string 
            val compare = String.compare) 

और हम उसके बाद उपयुक्त मॉड्यूल से फिटिंग समारोह का उपयोग करना चाहिए जब हम

val strSet = StrOrdSet.fromList ["a","b","c"] 
val intSet = IntOrdSet.fromList [1,2,3,4,5,6] 

वहाँ एक बिल्कुल स्पष्ट यहाँ समंजन है:: किसी दिए गए प्रकार पर काम करना चाहते हैं LIST मॉड्यूल कार्यों कि किसी भी प्रकार तुम पर लेकर कृपया प्रदान करते हैं, लेकिन वे किसी भी संबंध का लाभ नहीं ले जा सकते हैं कि किसी भी विशेष प्रकार के मूल्यों के बीच पकड़ो; ORD_SET मॉड्यूल कार्यों जरूरी है कि प्रकार functors पैरामीटर में आपूर्ति करने के लिए विवश कर रहे हैं प्रदान करते हैं लेकिन यह है कि एक ही parameterization के माध्यम से, वे आंतरिक संरचना के बारे में विशेष जानकारी और अपने लक्ष्य प्रकार के संबंधों को शामिल में सक्षम हैं।

यह मामलों की कल्पना जहां हम सूची मॉड्यूल के एक विकल्प के परिवार डिजाइन करने के लिए चाहते हो जाएगा, एक और अधिक जटिल संरचना के साथ के प्रकार और अन्य मूल्यों parameterize प्रदान करने के लिए सूची की तरह डेटा प्रकार functors का उपयोग करने के लिए आसान है: जैसे, निर्दिष्ट करने के लिए आदेशित सूची के लिए डेटा प्रकार, या स्व-संतुलन बाइनरी खोज पेड़ का उपयोग करके सूचियों का प्रतिनिधित्व करने के लिए।

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

सामान्य शब्दों में, मेरे सवाल यह है: जब मैं नाना प्रकार से संबंधित मॉड्यूल की एक प्रणाली है, डिजाइनिंग हूँ मैं बाहर कैसे लगा सकते हैं मॉड्यूल आसपास डिजाइन करने के लिए है कि क्या बहुरूपी कार्य या मॉड्यूल functors प्रकारों और मानों पर पैरामिट्रीकृत उपयोग करते हुए उत्पन्न प्रदान ?

मुझे दुविधा का वर्णन करने की उम्मीद है और यह एक उदाहरण के साथ क्यों मायने रखता है, एक खिलौना परियोजना से लिया गया है जिस पर मैं काम कर रहा हूं।

मेरे पास functor PostFix (ST:STACK) : CALCULATOR_SYNTAX है। यह एक स्टैक डेटा संरचना के कार्यान्वयन को लेता है और एक पार्सर बनाता है जो कंक्रीट पोस्टफिक्स ("रिवर्स पॉलिश") को एक अमूर्त वाक्यविन्यास में नोटेशन ( को कैलक्यूलेटर मॉड्यूल डाउन स्ट्रीम द्वारा मूल्यांकन किया जाता है), और इसके विपरीत। अब, मैं गया था एक मानक ढेर इंटरफ़ेस है कि उस पर संचालित करने के लिए एक बहुरूपी ढेर प्रकार और कार्यों का नंबर प्रदान करता है का उपयोग करते हुए:

signature STACK = 
sig 
    type 'a stack 
    exception EmptyStack 

    val empty : 'a stack 
    val isEmpty : 'a stack -> bool 

    val push : ('a * 'a stack) -> 'a stack 
    val pop : 'a stack -> 'a stack 
    val top : 'a stack -> 'a 
    val popTop : 'a stack -> 'a stack * 'a 
end 

यह ठीक काम करता है, और मुझे कुछ लचीलापन प्रदान करता है, जैसा कि मैंने एक सूची का उपयोग कर सकते आधारित स्टैक या वेक्टर-आधारित ढेर, या जो भी हो। लेकिन, मैं स्टैक मॉड्यूल में फ़ंक्शन को एक साधारण लॉगिंग जोड़ना चाहता हूं, ताकि जब भी कोई तत्व धक्का दिया जाए, या स्टैक से पॉप किया गया हो, तो यह स्टैक की वर्तमान स्थिति को प्रिंट करता है। अब मैं को स्टैक द्वारा एकत्र किए गए प्रकार के लिए fun toString : 'a -> string की आवश्यकता होगी, और , जैसा कि मैं समझता हूं, STACK मॉड्यूल में शामिल नहीं किया जा सकता है।अब मैं को मॉड्यूल में प्रकार को सील करने की आवश्यकता है, और स्टैक में एकत्र किए गए और toString फ़ंक्शन पर मॉड्यूल को पैरामीटर करें जो मुझे एकत्रित प्रकार के प्रिंट करने योग्य प्रतिनिधित्व का उत्पादन करने देगा। इसलिए मैं की तरह

functor StackFn (type t 
       val toString: t -> string) = 
struct 
    ... 
end 

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

तो, पर लौटने के लिए उनका विस्तार, और (शुक्र) मेरे सवाल खत्म:

  1. वहाँ StackFn तो द्वारा उत्पादित मॉड्यूल के हस्ताक्षर को निर्दिष्ट करने की किसी तरह से है कि वे के रूप में "पहुंच जाएंगे है STACK के विशेष मामले "?
  2. वैकल्पिक रूप से, वहाँ PostFix मॉड्यूल कि दोनों StackFn द्वारा उत्पादित मॉड्यूल और उन है कि STACK को संतुष्ट करने के लिए अनुमति होगी के लिए एक हस्ताक्षर लेखन का एक तरीका है?
  3. आम तौर पर, मॉड्यूल के बीच संबंध के बारे में सोचने का एक तरीका है जो मुझे भविष्य में इस तरह की चीज़ को पकड़ने/अनुमान लगाने में मदद करेगा?

(आप यह पढ़ गया है अब तक। आपको बहुत बहुत धन्यवाद!)

उत्तर

4

आप की खोज की हैं, वहाँ पैरामीट्रिक पोलिमोर्फ़िज्मके functors के/एसएमएल और OCaml में मॉड्यूल के बीच एक तनाव है। यह ज्यादातर मॉड्यूल की "दो भाषा" प्रकृति और विज्ञापन के बहुरूपता की कमी के कारण होता है। 1ML और modular implicits दोनों उस समस्या के विभिन्न समाधान प्रदान करते हैं। दो तरह के पैरामीटरवाद को एकजुट करके पहला, बाद में जब आवश्यक हो तो कुछ विज्ञापन-प्रसार बहुरूपता को चमकाने की अनुमति देकर।

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

module type POLYSTACK = sig 
    type 'a stack 
    exception EmptyStack 

    val empty : 'a stack 
    val isEmpty : 'a stack -> bool 

    val push : ('a * 'a stack) -> 'a stack 
    val pop : 'a stack -> 'a stack 
    val top : 'a stack -> 'a 
    val popTop : 'a stack -> 'a stack * 'a 
    val toString : ('a -> string) -> 'a stack -> string 
end 

module type STACK = sig 
    type elt 
    type t 
    exception EmptyStack 

    val empty : t 
    val isEmpty : t -> bool 

    val push : (elt * t) -> t 
    val pop : t -> t 
    val top : t -> elt 
    val popTop : t -> t * elt 
    val toString : t -> string 
end 

module type PRINTABLE = sig 
    type t 
    val toString : t -> string 
end 

module Make (E : PRINTABLE) (S : POLYSTACK) 
    : STACK with type elt = E.t and type t = E.t S.stack 
= struct 
    type elt = E.t 
    type t = E.t S.stack 
    include S 
    let toString = S.toString E.toString 
end 

module AddLogging (S : STACK) 
    : STACK with type elt = S.elt and type t = S.t 
= struct 
    include S 
    let push (x, s) = 
    let s' = S.push (x, s) in print_string (toString s') ; s' 
end 
+2

एक बहुत ही प्रचलित प्रश्न के लिए एक अद्भुत सटीक और संक्षिप्त उत्तर क्या है। आपका बहुत बहुत धन्यवाद। मैं बहुत लंबे समय से पहले 1 एमएल के साथ खेलने की उम्मीद करता हूं। मैं धीरे-धीरे * एफ-आईएनजी मॉड्यूल * पेपर के माध्यम से अपना रास्ता काम कर रहा हूं, और मुझे संदेह था कि तनाव यहां सार्वभौमिक और अस्तित्वहीन प्रकार के फंक्शंस और संरचनाओं (क्रमशः) के बीच अंतर से संबंधित है, लेकिन यह केवल एक झटका है । इस दिशा में मेरे शोध ने मुझे पॉलीटाइपिक प्रोग्रामिंग और टाइप-इंडेक्सिंग का नेतृत्व किया। क्या आप इनमें से दो दृष्टिकोणों से संबंधित हैं? –

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