सी # इंटरफ़ेस के साथ हस्केल टाइपकास्ट को लागू करें


13

मैं हास्केल के प्रकार की कक्षाओं और सी # के इंटरफेस की तुलना करने की कोशिश कर रहा हूं। मान लीजिए कि एक है Functor

हास्केल:

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

इस प्रकार के वर्ग को C # में इंटरफ़ेस के रूप में कैसे लागू किया जाए?

मैंने क्या कोशिश की है:

interface Functor<A, B>
{
    F<B> fmap(Func<A, B> f, F<A> x);
}

यह अमान्य क्रियान्वयन है और मैं वास्तव में जेनेरिक Fप्रकार के साथ फंस गया हूं जिसे वापस करना चाहिए fmap। इसे कैसे परिभाषित किया जाना चाहिए और कहां?

क्या FunctorC # में लागू करना असंभव है और क्यों? या शायद एक और दृष्टिकोण है?


8
एरिक लिपर्ट इस बारे में थोड़ी सी बात करते हैं कि सी # प्रकार की प्रणाली वास्तव में फंक्टरों की उच्च दयालु प्रकृति का समर्थन करने के लिए पर्याप्त नहीं है क्योंकि इस उत्तर में हास्केल द्वारा परिभाषित किया गया है: stackoverflow.com/a/4412319/303940
KChalouli

1
यह करीब 3 साल पहले की बात है। कुछ परिवर्तित है?
ДМИТРИЙ МАЛИКОВ

4
C # में इसे संभव बनाने के लिए कुछ भी नहीं बदला है, और न ही मुझे लगता है कि भविष्य में इसकी संभावना है
jk।

जवाबों:


8

C # प्रकार की प्रणाली में इंटरफ़ेस के रूप में प्रकार की कक्षाओं को ठीक से लागू करने के लिए आवश्यक कुछ सुविधाओं का अभाव है।

आइए अपने उदाहरण के साथ शुरू करें, लेकिन कुंजी एक फुलका खाता दिखा रही है कि टाइपकास्ट क्या है और क्या करता है, और फिर उन सी # बिट्स को मैप करने की कोशिश कर रहा है।

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

यह प्रकार वर्ग परिभाषा है, या इंटरफ़ेस के समान है। अब एक प्रकार की परिभाषा देखते हैं और यह उस प्रकार के वर्ग का कार्यान्वयन है।

data Awesome a = Awesome a a

instance Functor Awesome where
  fmap f (Awesome a1 a2) = Awesome (f a1) (f a2)

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

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

आप देखते हैं, हास्केल के साथ आप फंक्शंस को परिभाषित कर सकते हैं

data List a = List a (List a) | Terminal
data Tree a = Tree val (Tree a) (Tree a) | Terminal

instance Functor List where
  fmap :: (a -> b) -> List a -> List b
  fmap f (List a Terminal) = List (f a) Terminal
  fmap f (List a rest) = List (f a) (fmap f rest)

instance Functor Tree where
  fmap :: (a -> b) -> Tree a -> Tree b
  fmap f (Tree val Terminal Terminal) = Tree (f val) Terminal Terminal
  fmap f (Tree val Terminal right) = Tree (f val) Terminal (fmap f right)
  fmap f (Tree val left Terminal) = Tree (f val) (fmap f left) Terminal
  fmap f (Tree val left right) = Tree (f val) (fmap f left) (fmap f right)

तब खपत में आपका एक कार्य हो सकता है:

mapsSomething :: Functor f, Show a => f a -> f String
mapsSomething rar = fmap show rar

यहाँ समस्या है। C # में आप इस फंक्शन को कैसे लिखते हैं?

public Tree<a> : Functor<a>
{
    public a Val { get; set; }
    public Tree<a> Left { get; set; }
    public Tree<a> Right { get; set; }

    public Functor<b> fmap<b>(Func<a,b> f)
    {
        return new Tree<b>
        {
            Val = f(val),
            Left = Left.fmap(f);
            Right = Right.fmap(f);
        };
    }
}
public string Show<a>(Showwable<a> ror)
{
    return ror.Show();
}

public Functor<String> mapsSomething<a,b>(Functor<a> rar) where a : Showwable<b>
{
    return rar.fmap(Show<b>);
}

इसलिए सी # संस्करण के साथ कुछ गलत बातें हैं, एक बात के लिए मैं निश्चित भी नहीं हूं कि यह आपको <b>क्वालिफायर का उपयोग करने की अनुमति देगा जैसे मैंने वहां किया था, लेकिन इसके बिना मैं निश्चित हूं कि यह Show<>उचित रूप से प्रेषण नहीं होगा। और पता लगाने के लिए संकलन; मैंने नहीं किया)।

हालाँकि इससे भी बड़ी समस्या यह है कि हास्केल में ऊपर के विपरीत जहां हमने अपने Terminalप्रकार के हिस्से के रूप में परिभाषित किया था और फिर सी # उपयुक्त पैरामीट्रिक पॉलीमॉर्फिज्म की कमी के कारण टाइप के स्थान पर प्रयोग करने योग्य था (जो सुपर स्पष्ट हो जाता है जैसे ही आप इंटरॉप करने की कोशिश करते हैं F # के साथ C #) आप स्पष्ट रूप से या साफ-साफ भेद नहीं कर सकते हैं कि दाएं या बाएं Terminals हैं या नहीं । सबसे अच्छा आप उपयोग कर सकते हैं null, लेकिन क्या होगा यदि आप एक मूल्य प्रकार बनाने की कोशिश कर रहे हैं Functorया उस मामले में Eitherजहां आप दो प्रकारों को अलग कर रहे हैं कि दोनों एक मूल्य ले? अब आपको एक प्रकार का उपयोग करना होगा और अपने भेदभाव को मॉडल करने के लिए जांचने और स्विच करने के लिए दो अलग-अलग मूल्य होंगे?

उचित योग प्रकारों, यूनियन प्रकारों, ADTs की कमी, जो भी आप उन्हें कॉल करना चाहते हैं, वह वास्तव में बहुत कुछ बनाता है जो टाइपकालेज़ आपको देते हैं क्योंकि वे दिन के अंत में आपको कई प्रकारों (कंस्ट्रक्टर्स) को एक प्रकार का मानते हैं। और .NET की अंतर्निहित प्रकार प्रणाली में ऐसी कोई अवधारणा नहीं है।


2
मैं हास्केल (केवल मानक एमएल) में बहुत अच्छी तरह से वाकिफ नहीं हूं, इसलिए मुझे नहीं पता कि इससे कितना फर्क पड़ता है, लेकिन सी # में योग के प्रकारों को एनकोड करना संभव है
डोभाल

5

आपको दो वर्गों की आवश्यकता है, उच्च क्रम जेनेरिक (फ़नकार) मॉडल करने के लिए एक और फ़्री ए के साथ संयुक्त फ़नकार मॉडल बनाने के लिए एक

interface F<Functor> {
   IF<Functor, A> pure<A>(A a);
}

interface IF<Functor, A> where Functor : F<Functor> {
   IF<Functor, B> pure<B>(B b);
   IF<Functor, B> map<B>(Func<A, B> f);
}

इसलिए अगर हम ऑप्शन मोनाड का उपयोग करते हैं (क्योंकि सभी मोनाड फंक्शनल हैं)

class Option : F<Option> {
   IF<Option, A> pure<A>(A a) { return new Some<A>(a) };
}

class OptionF<A> : IF<Option, A> {
   IF<Option, B> pure<B>(B b) {
      return new Some<B>(b);
   }

   IF<Option, B> map<B>(Func<A, B> f) {
       var some = this as Some<A>;
       if (some != null) {
          return new Some<B>(f(some.value));
       } else {
          return new None<B>();
       }
   } 
}

जब आप की आवश्यकता हो तो आप IF <विकल्प, B> से कुछ <A> में बदलने के लिए स्थैतिक विस्तार विधियों का उपयोग कर सकते हैं


pureजेनेरिक फ़ंक्शनल इंटरफ़ेस में मुझे कठिनाइयाँ होती हैं : कम्पाइलर शिकायत करता है IF<Functor, A> pure<A>(A a);कि "टाइप को जेनेरिक प्रकार की विधि में Functorटाइप पैरामीटर के रूप में इस्तेमाल नहीं किया जा सकता । इसमें कोई बॉक्सिंग रूपांतरण या टाइप पैरामीटर रूपांतरण नहीं है ।" इसका क्या मतलब है? और हमें दो जगहों पर क्यों परिभाषित करना चाहिए ? इसके अलावा, स्थिर नहीं होना चाहिए ? FunctorIF<Functor, A>FunctorF<Functor>purepure
Niriel

1
नमस्ते। मुझे लगता है क्योंकि मैं क्लास डिजाइन करते समय मोनाड्स और मोनाड ट्रांसफॉर्मर की ओर इशारा कर रहा था। एक मोनाड ट्रांसफ़ॉर्मर, जैसे ऑप्शन टीटीए मॉनाड ट्रांसफ़ॉर्मर (एचएएसटीईएल में एचएसटी) को सी # में ऑप्शन टी <एम, ए> के रूप में परिभाषित किया गया है, जहां एम एक और सामान्य मोनड है। OptionT मोनड ट्रांसफॉर्मर बॉक्स में M <Option <A >> टाइप करें, लेकिन C # में उच्च प्रकार के प्रकार नहीं होते हैं, इसलिए आपको OptionT.map और OptionT.bind को कॉल करने के दौरान उच्च तरह के M मोन को इंस्टेंट करने का एक तरीका चाहिए। स्टेटिक तरीके इसलिए काम नहीं करते क्योंकि आप किसी भी मोनाड के लिए M.pure (A) नहीं कह सकते। M.
डेट्रायक्सएक्सआई
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.