टाइप क्लासेस बनाम ऑब्जेक्ट इंटरफेस


33

मुझे नहीं लगता कि मैं टाइप कक्षाएं समझता हूं। मैंने कहीं पढ़ा होगा कि टाइप क्लासेस को "इंटरफेस" (ओओ से) के रूप में माना जाता है कि एक टाइप इम्प्लीमेंट गलत और भ्रामक है। समस्या यह है, मुझे एक समस्या है जो उन्हें कुछ अलग और कैसे गलत है, के रूप में देख रही है।

उदाहरण के लिए, यदि मेरे पास एक प्रकार की कक्षा है (हास्केल सिंटैक्स में)

class Functor f where
  fmap :: (a -> b) -> f a -> f b

यह कैसे इंटरफ़ेस से अलग है [1] (जावा सिंटैक्स में)

interface Functor<A> {
  <B> Functor<B> fmap(Function<B, A> fn)
}

interface Function<Return, Argument> {
  Return apply(Argument arg);
}

एक संभावित अंतर जो मैं सोच सकता हूं, वह यह है कि एक निश्चित आह्वान पर इस्तेमाल किया जाने वाला प्रकार वर्ग कार्यान्वयन निर्दिष्ट नहीं है, बल्कि पर्यावरण से निर्धारित होता है - कहते हैं, इस प्रकार के कार्यान्वयन के लिए उपलब्ध मॉड्यूल की जांच करना। यह एक कार्यान्वयन कलाकृति प्रतीत होती है जिसे एक OO भाषा में संबोधित किया जा सकता है; जैसे कंपाइलर (या रनटाइम) रैपर / एक्सटेंडर / मंकी-पैच के लिए स्कैन कर सकता है जो कि टाइप पर आवश्यक इंटरफ़ेस को उजागर करता है।

मुझे किसकी याद आ रही है?

[१] ध्यान दें कि यह ओओ भाषा दिए f aजाने के fmapबाद से तर्क हटा दिया गया है , आप इस विधि को किसी वस्तु पर कॉल करेंगे। यह इंटरफ़ेस मानता है कि f aतर्क को ठीक कर दिया गया है।

जवाबों:


46

अपने मूल रूप में, प्रकार की कक्षाएं कुछ हद तक ऑब्जेक्ट इंटरफेस के समान हैं। हालांकि, कई मामलों में, वे बहुत अधिक सामान्य हैं।

  1. डिस्पैच प्रकार पर है, मूल्यों पर नहीं। इसे करने के लिए किसी मूल्य की आवश्यकता नहीं है। उदाहरण के लिए, परिणाम के प्रकार पर कार्य करना संभव है, जैसा कि हास्केल के Readवर्ग के साथ:

    class Read a where
      readsPrec :: Int -> String -> [(a, String)]
      ...
    

    पारंपरिक OO में ऐसा प्रेषण स्पष्ट रूप से असंभव है।

  2. प्रकार कक्षाएं स्वाभाविक रूप से कई प्रेषण तक विस्तृत होती हैं, बस कई पैरामीटर प्रदान करके:

    class Mul a b c where
      (*) :: a -> b -> c
    
    instance Mul Int Int Int where ...
    instance Mul Int Vec Vec where ...
    instance Mul Vec Vec Int where ...
    
  3. उदाहरण परिभाषाएं वर्ग और प्रकार दोनों परिभाषाओं से स्वतंत्र हैं, जो उन्हें अधिक मॉड्यूलर बनाती हैं। मॉड्यूल ए से एक प्रकार टी को मॉड्यूल एम 3 में एक उदाहरण प्रदान करके, या तो परिभाषा को संशोधित किए बिना मॉड्यूल एम 2 से एक वर्ग सी तक रेट्रोफिट किया जा सकता है। OO में, इसे और अधिक गूढ़ (और कम OO-ish) भाषा की आवश्यकता होती है जैसे विस्तार विधियाँ।

  4. टाइप कक्षाएं पैरामीट्रिक पॉलीमॉर्फिज़्म पर आधारित होती हैं, न कि सबटाइपिंग पर। यह अधिक सटीक टाइपिंग को सक्षम करता है। उदाहरण पर विचार करें

    pick :: Enum a => a -> a -> a
    pick x y = if fromEnum x == 0 then y else x
    

    बनाम

    pick(x : Enum, y : Enum) : Enum = if x.fromEnum() == 0 then y else x
    

    पूर्व मामले में, आवेदन करने pick '\0' 'x'का प्रकार है Char, जबकि बाद वाले मामले में, आप सभी को परिणाम के बारे में पता होगा कि यह एक एनम है। (यही कारण है कि इन दिनों अधिकांश OO भाषाएँ पैरामीट्रिक बहुरूपता को एकीकृत करती हैं।)

  5. बारीकी से संबंधित बाइनरी तरीकों का मुद्दा है। वे पूरी तरह से प्राकृतिक प्रकार की कक्षाओं के साथ हैं:

    class Ord a where
      (<) :: a -> a -> Bool
      ...
    
    min :: Ord a => a -> a -> a
    min x y = if x < y then x else y
    

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

    interface Comparable<T> {
      int compareTo(T y);
    };
    
    <T extends Comparable<T>> T min(T x, T y) {
      if (x.compareTo(y) < 0)
        return x;
      else
        return y;
    }
    

दूसरी ओर, उप-आधारित इंटरफेस स्वाभाविक रूप से विषम संग्रह के गठन की अनुमति देते हैं, उदाहरण के लिए प्रकार की एक सूची में List<C>ऐसे सदस्य शामिल हो सकते हैं जिनके विभिन्न उपप्रकार होते हैं C(हालांकि डाउनकास्ट का उपयोग करके, उनके सटीक प्रकार को पुनर्प्राप्त करना संभव नहीं है)। प्रकार वर्गों के आधार पर समान करने के लिए, आपको एक अतिरिक्त सुविधा के रूप में अस्तित्वगत प्रकारों की आवश्यकता होती है।


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

क्या अस्तित्वगत प्रकार कुछ डाउनकेन्स Cकी उपस्थिति के बिना उपप्रकार बनाने के लिए समान हैं ?
ओकोनोरो ०

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

PS: FWIW, Java में वाइल्डकार्ड प्रकार अस्तित्वपरक प्रकार हैं, हालांकि सीमित और तदर्थ (जो इस कारण का हिस्सा हो सकता है कि वे कुछ भ्रमित क्यों हैं)।
एंड्रियास रॉसबर्ग

1
@ डिडिएरेक, उन मामलों तक ही सीमित रहेगा जिन्हें पूरी तरह से वैधानिक रूप से हल किया जा सकता है। इसके अलावा, प्रकार की कक्षाओं से मेल खाने के लिए इसे ओवरलोडिंग रिज़ॉल्यूशन के एक प्रकार की आवश्यकता होगी जो अकेले रिटर्न प्रकार के आधार पर अंतर करने में सक्षम हो (आइटम देखें)।
एंड्रियास रॉसबर्ग

6

एंड्रियास के उत्कृष्ट उत्तर के अलावा, कृपया ध्यान रखें कि टाइप कक्षाएं ओवरलोडिंग को व्यवस्थित करने के लिए होती हैं , जो वैश्विक नाम स्थान को प्रभावित करती हैं। Haskell में कोई अतिभार नहीं है जो आप टाइप कक्षाओं के माध्यम से प्राप्त कर सकते हैं। इसके विपरीत, जब आप ऑब्जेक्ट इंटरफेस का उपयोग करते हैं, तो केवल उन फ़ंक्शन जो उस इंटरफ़ेस के तर्कों को लेने के लिए घोषित किए जाते हैं, उस इंटरफ़ेस में फ़ंक्शन नामों के बारे में चिंता करने की आवश्यकता होगी। इसलिए, इंटरफेस स्थानीय नाम स्थान प्रदान करते हैं।

उदाहरण के लिए, आपके पास fmap"फ़ंक्टर" नामक ऑब्जेक्ट इंटरफ़ेस में था । fmap"स्ट्रक्चरर" कहना एक अन्य इंटरफ़ेस में एक और होना पूरी तरह से ठीक होगा । प्रत्येक ऑब्जेक्ट (या वर्ग) चुन सकता है और यह चुन सकता है कि कौन सा इंटरफ़ेस लागू करना चाहता है। इसके विपरीत, हास्केल में, आप fmapएक विशेष संदर्भ में केवल एक ही हो सकते हैं । आप Functor और Structor दोनों प्रकार की कक्षाओं को एक ही संदर्भ में आयात नहीं कर सकते।

ऑब्जेक्ट इंटरफेस टाइप कक्षाओं की तुलना में मानक एमएल हस्ताक्षर के समान हैं।


और फिर भी एमएल मॉड्यूल और हास्केल प्रकार वर्गों के बीच घनिष्ठ संबंध प्रतीत होता है। cse.unsw.edu.au/~chak/papers/DHC07.html
स्टीवन शॉ

1

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

Maybe<Int> val = new Maybe<Int>(5);
Functor<Int> res = val.fmap(someFunctionHere);

ध्यान दें कि resटाइप फनकार है, शायद नहीं। इसलिए यह जावा कार्यान्वयन को लगभग अनुपयोगी बनाता है क्योंकि आप ठोस प्रकार की जानकारी खो देते हैं, और कास्ट करने की आवश्यकता होती है। (कम से कम मैं इस तरह के कार्यान्वयन को लिखने में विफल रहा जहां प्रकार अभी भी मौजूद थे)। हास्केल प्रकार की कक्षाओं के साथ आपको परिणाम के रूप में हो सकता है।


मुझे लगता है कि यह मुद्दा जावा के कारण उच्च प्रकार के प्रकारों का समर्थन नहीं करता है, और इंटरफेस बनाम टाइपकास्टेस चर्चा से संबंधित नहीं है। यदि जावा उच्च प्रकार का था, तो fmap बहुत अच्छी तरह से वापस आ सकता है Maybe<Int>
dcastro
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.