हां, सतह पर एक बहुत ही सीधा सवाल। लेकिन अगर आप इसे अंत तक सोचने का समय निकालते हैं, तो आप टाइप थ्योरी की गहराई में पहुंच जाते हैं। और टाइप थ्योरी भी आपको घूरती है।
सबसे पहले, निश्चित रूप से, आप पहले से ही सही ढंग से पता लगा चुके हैं कि एफ # में टाइप कक्षाएं नहीं हैं, और इसीलिए। लेकिन आप एक इंटरफ़ेस प्रस्तावित करते हैं Mappable। ठीक है, चलो उस पर गौर करें।
मान लें कि हम इस तरह के एक इंटरफेस की घोषणा कर सकते हैं। क्या आप सोच सकते हैं कि इसके हस्ताक्षर क्या दिखेंगे?
type Mappable =
abstract member map : ('a -> 'b) -> 'f<'a> -> 'f<'b>
fइंटरफ़ेस को लागू करने का प्रकार कहां है। अरे रुको! F # है कि या तो नहीं है! यहाँ fएक उच्च-प्रकार प्रकार है, और F # में उच्च-दयालुता नहीं है। कोई फ़ंक्शन घोषित करने का कोई तरीका नहीं हैf : 'm<'a> -> 'm<'b> या ऐसा कुछ है।
लेकिन ठीक है, मान लीजिए कि हम उस बाधा से भी पार हो गए। और अब हम एक अंतरफलक है Mappableकि द्वारा लागू किया जा सकता List, Array, Seq, और पानी के नल। लेकिन रुकें! अब हमारे पास फ़ंक्शन के बजाय एक विधि है , और विधियाँ अच्छी तरह से रचना नहीं करती हैं! आइए एक नेस्टेड सूची के प्रत्येक तत्व में 42 जोड़ते हुए देखें:
// Good ol' functions:
add42 nestedList = nestedList |> List.map (List.map ((+) 42))
// Using an interface:
add42 nestedList = nestedList.map (fun l -> l.map ((+) 42))
देखिए: अब हमें एक लंबोदर अभिव्यक्ति का उपयोग करना होगा! इसे पारित करने का कोई तरीका नहीं है.mapमूल्य के रूप में किसी अन्य फ़ंक्शन के लिए कार्यान्वयन को । प्रभावी रूप से "मूल्यों के रूप में कार्य" (और हां, मुझे पता है, एक लंबो का उपयोग करना इस उदाहरण में बहुत बुरा नहीं लगता है, लेकिन मुझ पर भरोसा करें, यह बहुत बदसूरत हो जाता है)
लेकिन रुकिए, हम अभी भी नहीं हुए हैं। अब जब कि यह एक विधि कॉल है, तो प्रकारान्तर से काम नहीं होता है! क्योंकि .NET विधि का प्रकार हस्ताक्षर वस्तु के प्रकार पर निर्भर करता है, इसलिए कंपाइलर के पास दोनों का अनुमान लगाने का कोई तरीका नहीं है। यह वास्तव में एक बहुत ही आम समस्या है newbies हिट जब। पुस्तकालयों के साथ interoperating। और एकमात्र इलाज एक प्रकार का हस्ताक्षर प्रदान करना है:
add42 (nestedList : #Mappable) = nestedList.map (fun l -> l.map ((+) 42))
ओह, लेकिन यह अभी भी पर्याप्त नहीं है! भले ही मैंने nestedListखुद के लिए एक हस्ताक्षर प्रदान किया हो, लेकिन मैंने लंबो के पैरामीटर के लिए एक हस्ताक्षर प्रदान नहीं किया है l। ऐसे हस्ताक्षर क्या होने चाहिए? क्या आप कहेंगे कि यह होना चाहिए fun (l: #Mappable) -> ...? ओह, और अब हम अंत में रैंक-एन प्रकार के लिए, आपके लिए देख रहे हैं, #Mappable"किसी भी प्रकार के लिए एक शॉर्टकट 'aहै 'a :> Mappable" - यानी एक लंबोदर अभिव्यक्ति जो स्वयं सामान्य है।
या, वैकल्पिक रूप से, हम उच्च-दयालुता पर वापस जा सकते हैं और nestedListअधिक सटीक प्रकार की घोषणा कर सकते हैं :
add42 (nestedList : 'f<'a<'b>> where 'f :> Mappable, 'a :> Mappable) = ...
लेकिन ठीक है, चलो अब के लिए एक प्रकार का अनुमान लगाते हैं और लैम्ब्डा अभिव्यक्ति पर वापस आते हैं और हम अब mapदूसरे फ़ंक्शन के मान के रूप में कैसे पारित नहीं कर सकते हैं । मान लें कि हम कुछ फ़ील्ड के लिए अनुमति देने के लिए वाक्यविन्यास को थोड़ा बढ़ाते हैं जैसे एल्म रिकॉर्ड फ़ील्ड के साथ क्या करता है:
add42 nestedList = nestedList.map (.map ((+) 42))
क्या प्रकार .mapहोगा? इसे हास्केल की तरह ही एक संकुचित प्रकार का होना चाहिए !
.map : Mappable 'f => ('a -> 'b) -> 'f<'a> -> 'f<'b>
वाह ठीक है। इस तथ्य को एक तरफ रखते हुए कि .NET इस प्रकार के अस्तित्व को भी अनुमति नहीं देता है, प्रभावी रूप से हम सिर्फ वापस प्रकार की कक्षाएं प्राप्त करते हैं!
लेकिन एक कारण है कि F # में पहली जगह पर टाइप की कक्षाएं नहीं हैं। उस कारण के कई पहलुओं को ऊपर वर्णित किया गया है, लेकिन इसे लागू करने का एक अधिक संक्षिप्त तरीका है: सरलता ।
आपके लिए, यह यार्न की एक गेंद है। एक बार जब आपके पास कक्षाएं होती हैं, तो आपको बाधाएं, उच्च-दयालुता, रैंक-एन (या कम से कम रैंक -2), और इससे पहले कि आप इसे जानते हैं, आप impredicative प्रकारों, प्रकार कार्यों, GADTs, और सभी के लिए पूछ रहे हैं इसके बाकी।
लेकिन हास्केल सभी अच्छाइयों के लिए एक कीमत चुकाता है। बारी-बारी से कोई अच्छा तरीका करने के लिए नहीं है बाहर का अनुमान लगा कि सभी सामान। उच्च-प्रकार के प्रकार सॉर्ट काम करते हैं, लेकिन पहले से ही थोड़े बाधाएं उत्पन्न नहीं होती हैं। रैंक-एन - इसका सपना भी नहीं। और जब यह काम करता है, तब भी आपको टाइप त्रुटियां मिलती हैं जिन्हें समझने के लिए आपको पीएचडी करनी होती है। और इसीलिए हास्केल में आपको धीरे-धीरे हर चीज पर टाइप हस्ताक्षर करने के लिए प्रोत्साहित किया जाता है। खैर, सब-कुछ नहीं , लेकिन वास्तव में लगभग सब कुछ। और जहां आप टाइप सिग्नेचर नहीं रखते हैं (जैसे अंदर letऔर बाहर where) - आश्चर्य-आश्चर्य, उन जगहों को वास्तव में मोनोमोर्फाइज्ड किया जाता है, इसलिए आप अनिवार्य रूप से सरलीकृत एफ # -land में वापस आ सकते हैं।
दूसरी ओर, F # में, प्रकार के हस्ताक्षर दुर्लभ हैं, ज्यादातर सिर्फ प्रलेखन के लिए या .NET इंटरॉप के लिए। उन दो मामलों में से, आप F # में पूरा बड़ा जटिल प्रोग्राम लिख सकते हैं और एक बार एक प्रकार के हस्ताक्षर का उपयोग नहीं कर सकते हैं। टाइप इंफ़ेक्शन ठीक काम करता है, क्योंकि इसे संभालने के लिए कुछ भी जटिल या अस्पष्ट नहीं है।
और हास्केल पर एफ # का यह बड़ा फायदा है। हां, हास्केल आपको सुपर कॉम्प्लेक्स स्टफ को बहुत सटीक तरीके से व्यक्त करने देता है, यह अच्छा है। लेकिन F # आपको अजगर या रूबी की तरह बहुत इच्छाधारी-अभ्यस्त बनाता है, और अगर आप ठोकर खाते हैं, तब भी आपके पास कंपाइलर है।