कब उच्च प्रकार उपयोगी होते हैं?


88

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

(मुझे लगता है) मुझे लगता है कि myList |> List.map fया myList |> Seq.map f |> Seq.toListउच्च प्रकार के बजाय आपको बस लिखने की अनुमति मिलती है myList |> map fऔर यह वापस आ जाएगी List। यह बहुत अच्छा है (यह मानते हुए कि यह सही है), लेकिन एक तरह से छोटा लगता है? (और यह केवल समारोह अतिभारित करने की अनुमति देकर नहीं किया जा सकता है?) मैं आमतौर पर Seqकिसी भी तरह से परिवर्तित करता हूं और फिर मैं जो कुछ भी चाहता हूं उसे बाद में परिवर्तित कर सकता हूं। फिर, शायद मैं अभी भी इसके आसपास काम कर रहा हूँ। लेकिन क्या कोई ऐसा उदाहरण है जहां उच्च-प्रकार के प्रकार वास्तव में कीस्ट्रोक्स या प्रकार की सुरक्षा में आपको बचाता है?


2
Control.Monad में कई कार्य उच्च प्रकार का उपयोग करते हैं ताकि आप कुछ उदाहरणों के लिए वहां देखना चाहें। एफ # में प्रत्येक ठोस मोनड प्रकार के लिए कार्यान्वयन को दोहराया जाना होगा।
ली

1
@ देखें, लेकिन क्या आप केवल एक इंटरफ़ेस नहीं बना सकते हैं IMonad<T>और फिर इसे वापस कर सकते हैं उदाहरण के लिए IEnumerable<int>या IObservable<int>जब आप कर रहे हैं? क्या यह सब सिर्फ कास्टिंग से बचने के लिए है?
लॉबस्टरवाद

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

4
@ हाँ, मैं सिर्फ सोच रहा था कि आपको अभिव्यक्ति के बाद अंतिम परिणाम देना होगा, कोई बड़ी बात नहीं है क्योंकि आपने सिर्फ अभिव्यक्ति की है इसलिए आप प्रकार जानते हैं। लेकिन ऐसा लगता है कि आपको bindउर्फ SelectManyइत्यादि के प्रत्येक भाग के अंदर भी डालना होगा । किसी को एपीआई का उपयोग कर सकता है जिसका मतलब है bindएक IObservableएक करने के लिए IEnumerableऔर, यह काम करेगा मान जो हाँ छी यदि ऐसा है और वहाँ है कि चारों ओर कोई रास्ता नहीं है। बस 100% यकीन नहीं है कि इसके आसपास कोई रास्ता नहीं है।
लॉबस्टरवाद

5
बड़ा सवाल है। मुझे अभी तक इस भाषा सुविधा का एक भी सम्मोहक व्यावहारिक उदाहरण उपयोगी IRL दिख रहा है।
जेडी

जवाबों:


78

तो एक प्रकार का प्रकार इसका सरल प्रकार है। उदाहरण के लिए Intदयालु है *जिसका अर्थ है कि यह एक आधार प्रकार है और इसे मूल्यों द्वारा त्वरित किया जा सकता है। उच्च-प्रकार के प्रकार की कुछ ढीली परिभाषा के द्वारा (और मुझे यकीन नहीं है कि F # लाइन कहां खींचता है, तो चलिए इसे शामिल करते हैं) पॉलीमॉर्फिक कंटेनर एक उच्च-दयालु प्रकार का एक शानदार उदाहरण है।

data List a = Cons a (List a) | Nil

टाइप कंस्ट्रक्टर Listमें दयालुता * -> *होती है, जिसका अर्थ है कि इसे ठोस प्रकार में परिणामित करने के लिए एक ठोस प्रकार पास किया जाना चाहिए: List Intनिवासी जैसे हो सकते हैं [1,2,3]लेकिन Listस्वयं नहीं कर सकते।

मैं यह मानने जा रहा हूं कि बहुरूपी कंटेनरों के लाभ स्पष्ट हैं, लेकिन अधिक उपयोगी प्रकार * -> *केवल कंटेनर की तुलना में मौजूद हैं। मसलन, संबंध

data Rel a = Rel (a -> a -> Bool)

या पार्सर

data Parser a = Parser (String -> [(a, String)])

दोनों भी दयालु हैं * -> *


हम इसे हास्केल में आगे ले जा सकते हैं, हालांकि, उच्च-प्रकार के प्रकारों के साथ भी। उदाहरण के लिए हम एक प्रकार के प्रकार के साथ देख सकते हैं (* -> *) -> *। इसका एक सरल उदाहरण यह हो सकता है Shapeजो कंटेनर के प्रकार को भरने की कोशिश करता है * -> *

data Shape f = Shape (f ())

[(), (), ()] :: Shape List

Traversableउदाहरण के लिए, हास्केल में एस को चिह्नित करने के लिए यह उपयोगी है , क्योंकि उन्हें हमेशा अपने आकार और सामग्री में विभाजित किया जा सकता है।

split :: Traversable t => t a -> (Shape t, [a])

एक अन्य उदाहरण के रूप में, आइए एक पेड़ पर विचार करें जो उस तरह की शाखा पर परिचालित है। उदाहरण के लिए, एक सामान्य पेड़ हो सकता है

data Tree a = Branch (Tree a) a (Tree a) | Leaf

लेकिन हम देख सकते हैं कि शाखा प्रकार एक शामिल Pairकी Tree aहै और इसलिए हम parametrically प्रकार से बाहर है कि टुकड़ा निकाल सकते हैं

data TreeG f a = Branch a (f (TreeG f a)) | Leaf

data Pair a = Pair a a
type Tree a = TreeG Pair a

इस TreeGप्रकार के कंस्ट्रक्टर में दयालुता है (* -> *) -> * -> *। हम इसका उपयोग एक की तरह दिलचस्प अन्य विविधताएं बनाने के लिए कर सकते हैंRoseTree

type RoseTree a = TreeG [] a

rose :: RoseTree Int
rose = Branch 3 [Branch 2 [Leaf, Leaf], Leaf, Branch 4 [Branch 4 []]]

या पैथोलॉजिकल वाले जैसे MaybeTree

data Empty a = Empty
type MaybeTree a = TreeG Empty a

nothing :: MaybeTree a
nothing = Leaf

just :: a -> MaybeTree a
just a = Branch a Empty

या ए TreeTree

type TreeTree a = TreeG Tree a

treetree :: TreeTree Int
treetree = Branch 3 (Branch Leaf (Pair Leaf Leaf))

एक अन्य स्थान जो यह दिखाता है कि वह "एलेग्राफर ऑफ़ फ़ंक्शनलर्स" में है। यदि हम अमूर्तता की कुछ परतों को गिराते हैं तो इसे बेहतर रूप में गुना माना जा सकता है, जैसे कि sum :: [Int] -> Int। बीजगणित को फफूंद और वाहक पर परिचालित किया जाता है । Functor तरह है * -> *और वाहक तरह *तो कुल मिलाकर

data Alg f a = Alg (f a -> a)

दयालु है (* -> *) -> * -> *Algडेटाटाइप और उसके निर्माण के लिए पुनरावृत्ति योजनाओं के संबंध में उपयोगी होने के कारण।

-- | The "single-layer of an expression" functor has kind `(* -> *)`
data ExpF x = Lit Int
            | Add x x
            | Sub x x
            | Mult x x

-- | The fixed point of a functor has kind `(* -> *) -> *`
data Fix f = Fix (f (Fix f))

type Exp = Fix ExpF

exp :: Exp
exp = Fix (Add (Fix (Lit 3)) (Fix (Lit 4))) -- 3 + 4

fold :: Functor f => Alg f a -> Fix f -> a
fold (Alg phi) (Fix f) = phi (fmap (fold (Alg phi)) f)

अंत में, हालांकि वे सैद्धांतिक रूप से संभव हैं, मैंने कभी भी एक उच्चतर प्रकार के निर्माता को नहीं देखा है। हम कभी-कभी उस प्रकार के कार्यों को देखते हैं mask :: ((forall a. IO a -> IO a) -> IO b) -> IO b, लेकिन मुझे लगता है कि आपको उस प्रकार की जटिलता के स्तर को देखने के लिए टाइप प्रोलॉग या भरोसेमंद टाइप साहित्य में खुदाई करनी होगी।


3
मैं कुछ मिनटों में कोड को टाइप-चेक करूँगा और संपादित करूँगा, मैं अभी अपने फोन पर हूँ।
जे। अब्राहमसन

12
@ J.Abrahamson +1 एक अच्छे उत्तर के लिए और आपके फ़ोन O_o पर टाइप करने के लिए धैर्य रखने के लिए
डैनियल ग्रैज़र

3
@lobsterism A TreeTreeसिर्फ पैथोलॉजिकल है, लेकिन अधिक व्यावहारिक रूप से इसका मतलब है कि आपको दो अलग-अलग प्रकार के पेड़ आपस में एक-दूसरे से जुड़े हुए हैं- इस विचार को थोड़ा आगे बढ़ाकर आप कुछ बहुत ही शक्तिशाली प्रकार-सुरक्षित धारणाएं प्राप्त कर सकते हैं जैसे कि स्टेटिक-सेफ रेड / काले पेड़ और साफ तौर पर संतुलित फिंगरट्री टाइप।
जे। अब्राहमसन

3
@JonHarrop एक मानक वास्तविक दुनिया का उदाहरण मठों पर अमूर्त है, उदाहरण के लिए mtl- शैली प्रभाव के ढेर के साथ। आप इस बात से सहमत नहीं हो सकते कि यह वास्तविक दुनिया है, हालांकि मूल्यवान है। मुझे लगता है कि यह आमतौर पर स्पष्ट है कि भाषाएं एचकेटी के बिना सफलतापूर्वक मौजूद हो सकती हैं, इसलिए कोई भी उदाहरण किसी प्रकार का अमूर्त प्रदान करेगा जो अन्य भाषाओं की तुलना में अधिक परिष्कृत है।
जे। अब्राहमसन

2
आप कर सकते हैं, उदाहरण के लिए विभिन्न भिक्षुओं में अधिकृत प्रभाव का उपसमुच्चय और उस विनिर्देशन को पूरा करने वाले किसी भी साधु पर सार। उदाहरण के लिए, मोनाड्स "टेलेटाइप" को तात्कालिक करने में सक्षम बनाता है, जो चरित्र स्तर को पढ़ने और लिखने में सक्षम करता है, जिसमें IO और पाइप अमूर्त दोनों शामिल हो सकते हैं। आप एक अन्य उदाहरण के रूप में विभिन्न अतुल्यकालिक कार्यान्वयन पर अमूर्त कर सकते हैं। एचकेटी के बिना आप उस सामान्य टुकड़े से बने किसी भी प्रकार को सीमित करते हैं।
जे। अब्राहमसन

64

Functorहास्केल में प्रकार वर्ग पर विचार करें , जहां fएक उच्च-प्रकार का प्रकार चर है:

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

क्या इस प्रकार के हस्ताक्षर का कहना है कि FMAP एक के प्रकार के पैरामीटर बदल जाता है fसे aकरने के लिए b, लेकिन पत्ते fके रूप में यह किया गया था। इसलिए यदि आप fmapएक सूची का उपयोग करते हैं, तो आपको एक सूची मिलती है, यदि आप इसका उपयोग किसी ऐसे पार्सर से करते हैं जो आपको पार्सर मिलता है, और इसी तरह। और ये स्थिर , संकलन-समय की गारंटी हैं।

मुझे F # नहीं पता है, लेकिन आइए विचार करें कि क्या होता है अगर हम Functorविरासत या जेनरिक के साथ जावा या सी # जैसी भाषा में अमूर्तता को व्यक्त करने की कोशिश करते हैं , लेकिन कोई उच्चतर किस्म का जेनरिक नहीं। पहली कोशिश:

interface Functor<A> {
    Functor<B> map(Function<A, B> f);
}

इस पहली कोशिश के साथ समस्या यह है कि इंटरफ़ेस के कार्यान्वयन को लागू करने वाले किसी भी वर्ग को वापस करने की अनुमति है Functor। कोई व्यक्ति लिख सकता है कि FunnyList<A> implements Functor<A>किसकी mapविधि एक अलग तरह का संग्रह लौटाती है, या कुछ और भी जो संग्रह बिल्कुल नहीं है, लेकिन फिर भी एक है Functor। इसके अलावा, जब आप mapविधि का उपयोग करते हैं तो आप परिणाम पर किसी भी उप-विशिष्ट तरीके को लागू नहीं कर सकते हैं जब तक कि आप इसे उस प्रकार से डाउनकास्ट नहीं करते हैं जो आप उम्मीद कर रहे हैं। इसलिए हमें दो समस्याएं हैं:

  1. प्रकार प्रणाली हमें अपरिवर्तनीय व्यक्त करने की अनुमति नहीं देती है कि mapविधि हमेशा समान होती हैFunctor रिसीवर के रूप में उपवर्ग ।
  2. इसलिए, Functorपरिणाम के आधार पर गैर- विधि को लागू करने के लिए कोई सांख्यिकीय प्रकार-सुरक्षित तरीका नहीं है map

अन्य, अधिक जटिल तरीके हैं जिन्हें आप आज़मा सकते हैं, लेकिन उनमें से कोई भी वास्तव में काम नहीं करता है। उदाहरण के लिए, आप Functorपरिणाम प्रकार को प्रतिबंधित करने वाले उप-प्रकारों को परिभाषित करके पहले प्रयास को बढ़ाने का प्रयास कर सकते हैं :

interface Collection<A> extends Functor<A> {
    Collection<B> map(Function<A, B> f);
}

interface List<A> extends Collection<A> {
    List<B> map(Function<A, B> f);
}

interface Set<A> extends Collection<A> {
    Set<B> map(Function<A, B> f);
}

interface Parser<A> extends Functor<A> {
    Parser<B> map(Function<A, B> f);
}

// …

यह उन संकरा इंटरफेस के कार्यान्वयनकर्ताओं Functorको mapविधि से गलत प्रकार को वापस करने से रोकने में मदद करता है , लेकिन चूंकि Functorआपके पास कितने कार्यान्वयन हो सकते हैं, इसकी कोई सीमा नहीं है, आपको कितनी संकरा इंटरफेस की आवश्यकता होगी, इसकी कोई सीमा नहीं है।

( संपादित करें: और ध्यान दें कि यह केवल इसलिए काम करता है क्योंकि Functor<B>परिणाम प्रकार के रूप में प्रकट होता है, और इसलिए बच्चे के इंटरफेस इसे संकीर्ण कर सकते हैं। इसलिए AFAIK हम Monad<B>निम्नलिखित इंटरफ़ेस के दोनों उपयोगों को संकीर्ण नहीं कर सकते हैं :

interface Monad<A> {
    <B> Monad<B> flatMap(Function<? super A, ? extends Monad<? extends B>> f);
}

हास्केल में, उच्च-रैंक प्रकार चर के साथ, यह है (>>=) :: Monad m => m a -> (a -> m b) -> m b।)

फिर भी एक और प्रयास करने के लिए पुनरावर्ती जेनेरिक का उपयोग करना है और इंटरफ़ेस के परिणाम प्रकार को उपप्रकार को केवल उपप्रकार तक सीमित रखना है। खिलौना उदाहरण:

/**
 * A semigroup is a type with a binary associative operation.  Law:
 *
 * > x.append(y).append(z) = x.append(y.append(z))
 */
interface Semigroup<T extends Semigroup<T>> {
    T append(T arg);
}

class Foo implements Semigroup<Foo> {
    // Since this implements Semigroup<Foo>, now this method must accept 
    // a Foo argument and return a Foo result. 
    Foo append(Foo arg);
}

class Bar implements Semigroup<Bar> {
    // Any of these is a compilation error:

    Semigroup<Bar> append(Semigroup<Bar> arg);

    Semigroup<Foo> append(Bar arg);

    Semigroup append(Bar arg);

    Foo append(Bar arg);

}

लेकिन इस तरह की तकनीक (जो आपके रन-ऑफ-द-मिल OOP डेवलपर के लिए समान है, आपके रन-ऑफ-द-मिल कार्यात्मक डेवलपर के लिए भी) अभी भी वांछित Functorबाधा व्यक्त नहीं कर सकती है :

interface Functor<FA extends Functor<FA, A>, A> {
    <FB extends Functor<FB, B>, B> FB map(Function<A, B> f);
}

समस्या यहाँ यह सीमित नहीं करता है FBएक ही है करने के लिए Fके रूप में FAहै, ताकि जब आप एक प्रकार घोषणा करते हैं कि List<A> implements Functor<List<A>, A>, mapविधि कर सकते हैं अभी भी एक वापसी NotAList<B> implements Functor<NotAList<B>, B>

अंतिम प्रयास, जावा में, कच्चे प्रकार (अनमीट्रीड कंटेनर) का उपयोग करके:

interface FunctorStrategy<F> {
    F map(Function f, F arg);
} 

यहाँ Fबस Listया जैसे अनपेक्षित तरीके से त्वरित मिलेगा Map। यह गारंटी देता है कि एक FunctorStrategy<List>ही वापस आ सकता हैList सूची के तत्व प्रकारों को ट्रैक करने के लिए आप हैं-आपने प्रकार चर का उपयोग छोड़ दिया है।

यहाँ समस्या का दिल यह है कि जावा और सी # जैसी भाषाएं मापदंडों को टाइप करने की अनुमति नहीं देती हैं। जावा में, यदि Tएक प्रकार का चर है, तो आप लिख सकते हैं Tऔर List<T>, लेकिन नहीं T<String>। उच्च-प्रकार के प्रकार इस प्रतिबंध को हटा देते हैं, ताकि आपके पास ऐसा कुछ हो (पूरी तरह से सोचा नहीं गया):

interface Functor<F, A> {
    <B> F<B> map(Function<A, B> f);
}

class List<A> implements Functor<List, A> {

    // Since F := List, F<B> := List<B>
    <B> List<B> map(Function<A, B> f) {
        // ...
    }

}

और इस बिट को विशेष रूप से संबोधित करते हुए:

(मुझे लगता है) मुझे लगता है कि myList |> List.map fया myList |> Seq.map f |> Seq.toListउच्च प्रकार के बजाय आपको बस लिखने की अनुमति मिलती है myList |> map fऔर यह वापस आ जाएगी List। यह बहुत अच्छा है (यह मानते हुए कि यह सही है), लेकिन एक तरह से छोटा लगता है? (और यह केवल समारोह अतिभारित करने की अनुमति देकर नहीं किया जा सकता है?) मैं आमतौर पर Seqकिसी भी तरह से परिवर्तित करता हूं और फिर मैं जो कुछ भी चाहता हूं उसे बाद में परिवर्तित कर सकता हूं।

कई भाषाएं हैं जो mapफ़ंक्शन के विचार को इस तरह से सामान्य करती हैं, जैसे कि यह मॉडलिंग करके, दिल से, मानचित्रण दृश्यों के बारे में है। आपकी यह टिप्पणी उस भावना में है: यदि आपके पास एक ऐसा प्रकार है जो Seqपुन: उपयोग करने के लिए "मुफ्त में" मानचित्र संचालन प्राप्त करता है Seq.map

हास्केल में, हालांकि, Functorवर्ग उससे अधिक सामान्य है; यह दृश्यों की धारणा से बंधा नहीं है। आप उन fmapप्रकारों के लिए कार्यान्वित कर सकते हैं जिनमें अनुक्रमों के लिए कोई अच्छा मानचित्रण नहीं है, जैसे कि IOक्रियाएं, पार्सर कॉम्बिनेटर, फ़ंक्शन, आदि।:

instance Functor IO where
    fmap f action =
        do x <- action
           return (f x)

 -- This declaration is just to make things easier to read for non-Haskellers 
newtype Function a b = Function (a -> b)

instance Functor (Function a) where
    fmap f (Function g) = Function (f . g)  -- `.` is function composition

"मैपिंग" की अवधारणा वास्तव में अनुक्रम से जुड़ी नहीं है। फ़नकार कानूनों को समझना सबसे अच्छा है:

(1) fmap id xs == xs
(2) fmap f (fmap g xs) = fmap (f . g) xs

बहुत अनौपचारिक रूप से:

  1. पहला कानून कहता है कि किसी पहचान / नूप फ़ंक्शन के साथ मैपिंग कुछ भी नहीं करने के समान है।
  2. दूसरा कानून कहता है कि कोई भी परिणाम जो आप दो बार मैप करके पैदा कर सकते हैं, आप एक बार मैप करके भी उत्पादन कर सकते हैं।

यही कारण है कि आप fmapप्रकार को संरक्षित करना चाहते हैं - क्योंकि जैसे ही आपको ऐसे mapऑपरेशन मिलते हैं जो एक अलग परिणाम प्रकार का उत्पादन करते हैं, यह इस तरह की गारंटी देने के लिए बहुत, बहुत कठिन हो जाता है।


इसलिए मैं अपने पिछले बिट में रुचि रही है, कारण है कि यह एक है के लिए उपयोगी है fmapपर Function aजब यह पहले से ही एक है .आपरेशन? मैं समझता हूं कि ऑप .की परिभाषा क्यों समझ में आती है fmap, लेकिन मुझे अभी वह स्थान नहीं मिला जहां आपको fmapइसके बजाय उपयोग करने की आवश्यकता होगी .। हो सकता है कि अगर आप एक उदाहरण दे सकते हैं कि यह कहां उपयोगी होगा, तो यह मुझे समझने में मदद करेगा।
लॉबस्टरवाद

1
आह, यह मिल गया: यदि आप एक fn कर सकते हैं doubleएक functor है, जहां के double [1, 2, 3]लिए देता है [2, 4, 6]और double sinएक fn कि डबल पाप है देता है। मैं देख सकता हूं कि अगर आप उस मानसिकता में सोचना शुरू करते हैं, जब आप एक सरणी पर एक नक्शा चलाते हैं, तो आप एक सरणी की उम्मीद करते हैं, न कि सिर्फ एक seq, क्योंकि, ठीक है, हम यहां सरणियों पर काम कर रहे हैं।
लॉबस्टरवाद

@lobsterism: एल्गोरिदम / तकनीकें हैं जो एक सार को बाहर करने में सक्षम होने पर भरोसा करती हैं Functorऔर लाइब्रेरी के क्लाइंट को इसे बाहर निकालने देती हैं। जे। अब्राहमसन का जवाब एक उदाहरण प्रदान करता है: पुनरावर्ती सिलवटों को फंक्शनलर्स का उपयोग करके सामान्य किया जा सकता है। एक और उदाहरण मुक्त मठ है; आप इन्हें एक प्रकार के सामान्य दुभाषिया कार्यान्वयन पुस्तकालय के रूप में सोच सकते हैं, जहां ग्राहक "अनुदेश सेट" को एक मनमाना के रूप में आपूर्ति करता है Functor
लुइस कैसिलस

3
एक तकनीकी रूप से ध्वनि उत्तर लेकिन यह मुझे आश्चर्यचकित करता है कि कोई भी कभी भी व्यवहार में ऐसा क्यों चाहेगा। मैंने खुद को हास्केल Functorया ए के लिए नहीं पाया है SemiGroup। वास्तविक कार्यक्रम इस भाषा की सुविधा का सबसे अधिक उपयोग कहां करते हैं?
जेडी

28

मैं पहले से ही यहाँ कुछ उत्कृष्ट उत्तरों में जानकारी दोहराना नहीं चाहता, लेकिन एक महत्वपूर्ण बिंदु है जिसे मैं जोड़ना चाहूंगा।

आपको आमतौर पर किसी एक विशेष मोनाड, या फ़ंक्टर (या ऐप्लिकेटर फ़ंक्टर, या एरो, या ...) को लागू करने के लिए उच्च-प्रकार के प्रकार की आवश्यकता नहीं होती है। लेकिन ऐसा करना ज्यादातर बिंदु को याद कर रहा है।

सामान्य तौर पर मैंने पाया है कि जब लोग फंक्शनलर्स / मोनाड्स / व्हाईवर्स की उपयोगिता नहीं देखते हैं, तो अक्सर ऐसा होता है क्योंकि वे एक समय में इन चीजों के बारे में सोच रहे होते हैं । फ़ंक्टर / मोनाड / आदि संचालन वास्तव में किसी भी एक उदाहरण के लिए कुछ भी नहीं जोड़ते हैं (इसके बजाय बाइंड, फ़ैप, आदि को कॉल कर सकते हैं) मैं केवल उन कार्यों को कॉल कर सकता हूं जो मैं बाँध, फ़ैप, आदि को लागू करने के लिए उपयोग करता था । क्या आप वास्तव में इन अमूर्त के लिए चाहते हैं ताकि आप कोड है कि किसी भी functor / monad / etc के साथ उदारता से काम कर सकते हैं ।

ऐसे संदर्भ में जहां इस तरह के सामान्य कोड का व्यापक रूप से उपयोग किया जाता है, इसका मतलब है कि किसी भी समय आप एक नया मोनाड लिखते हैं, जो आपके प्रकार तुरंत बड़ी संख्या में उपयोगी संचालन तक पहुंच प्राप्त करता है जो आपके लिए पहले ही लिखे जा चुके हैंऐसा इसलिए है monads देखने का बिंदु हर जगह (functors के, और ...); ऐसा नहीं है कि मैं उपयोग करने के bindबजाय concatऔर mapलागू कर सकता हूं myFunkyListOperation(जो मुझे अपने आप में कुछ भी हासिल नहीं करता है), बल्कि इसलिए कि जब मुझे जरूरत हो myFunkyParserOperationऔर myFunkyIOOperationमैं उन कोड का फिर से उपयोग कर सकूं जिन्हें मैंने मूल रूप से सूचियों के संदर्भ में देखा था क्योंकि यह वास्तव में सनक-सामान्य है ।

लेकिन एक पैरामीटर प्रकार के सार के साथ सार करने के लिए प्रकार की सुरक्षा के साथ एक साधु की तरह, आपको उच्च-प्रकार के प्रकार (साथ ही अन्य उत्तरों में समझाया गया है) की आवश्यकता है।


9
यह मेरे द्वारा अब तक पढ़े गए किसी भी अन्य उत्तर की तुलना में एक उपयोगी उत्तर होने के करीब है, लेकिन मैं अभी भी एक एकल व्यावहारिक अनुप्रयोग देखना चाहूंगा जहां उच्च प्रकार उपयोगी हो।
जेडी

"क्या आप वास्तव में इन सार चाहते हैं के लिए है ताकि आप कोड है कि किसी भी functor / monad के साथ उदारता से काम करता है" हो सकता है। एफ # को 13 साल पहले अभिकलन भाव के रूप में भिक्षुओं को मिला, मूल रूप से सेक् और एसिंक्स मोनड्स को स्पोर्ट करना। आज F # को 3 मोनाड, क्वेरी प्राप्त है। इतने कम संन्यासी जिनके पास इतने कम हैं, आप उन पर अमल क्यों करना चाहेंगे?
जद

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

"6 साल पुरानी पोस्ट पर एक बहस शुरू करने के लिए वापस आने से लाभ की उम्मीद"। पूर्वव्यापी। अब हम जानते हैं कि भिक्षुओं के ऊपर F # का सार काफी हद तक अप्रयुक्त रहता है। इसलिए 3 से अधिक भिन्न चीजों को अमूर्त करने की क्षमता अप्रकाशित है।
जेडी

@JonHarrop मेरे उत्तर की बात यह है कि व्यक्तिगत मोनाड (या फ़ंक्शनलर्स, या आदि) वास्तव में एक खानाबदोश इंटरफ़ेस के बिना व्यक्त की गई समान कार्यक्षमता की तुलना में अधिक उपयोगी नहीं है, लेकिन यह बहुत सी असमान चीजों को एकीकृत करता है। मैं F # पर आपकी विशेषज्ञता को टाल दूंगा, लेकिन अगर आप कह रहे हैं कि इसमें केवल 3 व्यक्तिगत मुद्राएं हैं (सभी अवधारणाओं के लिए एक राक्षसी इंटरफ़ेस को लागू करने के बजाय, जो एक हो सकती है, जैसे विफलता, राज्य-योग्यता, पार्सिंग, आदि), तो हां, यह बहुत ही आश्चर्यजनक है कि आपको उन 3 चीजों को एकजुट करने से ज्यादा फायदा नहीं होगा।
बेन

15

एक अधिक .NET-विशिष्ट परिप्रेक्ष्य के लिए, मैंने कुछ समय पहले इस बारे में एक ब्लॉग पोस्ट लिखा था । इसका क्रैक्स उच्च-प्रकार के प्रकारों के साथ है, आप संभवतः उसी LINQ ब्लॉक IEnumerablesऔर के बीच पुन: उपयोग कर सकते हैंIObservables , लेकिन उच्च-प्रकार के प्रकारों के बिना यह असंभव है।

निकटतम आप (मैं ब्लॉग पोस्ट करने के बाद पता लगा) मिल सकता है अपने स्वयं के बनाने के लिए है IEnumerable<T>और IObservable<T>और उन दोनों को एक से बढ़ाया IMonad<T>। यह आपको अपने LINQ ब्लॉकों का पुन: उपयोग करने की अनुमति देगा IMonad<T>, यदि वे निरूपित हैं , लेकिन तब यह अब टाइप करने योग्य नहीं है क्योंकि यह आपको मिक्स-इन-मैच IObservablesऔर करने की अनुमति देता हैIEnumerables उसी ब्लॉक के भीतर , जो इसे सक्षम करने के लिए पेचीदा लग सकता है, तो आप मूल रूप से बस कुछ अपरिभाषित व्यवहार मिलता है।

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


2
मैं आपको केवल एक उत्तर देने के लिए +1 दूंगा जिसमें कुछ व्यावहारिक का उल्लेख है लेकिन मुझे नहीं लगता कि मैंने कभी IObservablesउत्पादन कोड में उपयोग किया है।
जेडी

5
@JonHarrop यह असत्य लगता है। F # में सभी ईवेंट हैं IObservable, और आप अपनी स्वयं की पुस्तक के WinForms अध्याय में ईवेंट का उपयोग करते हैं।
डेक्स फ़ोहल

1
Microsoft ने मुझे उस पुस्तक को लिखने के लिए भुगतान किया और मुझे उस सुविधा को कवर करने की आवश्यकता पड़ी। मुझे उत्पादन कोड में घटनाओं का उपयोग करना याद नहीं है लेकिन मैं देखूंगा।
जेडी

IQueryable और IEnumerable के बीच पुन: उपयोग संभव होगा भी मुझे लगता है कि
KolA

चार साल बाद और मैं देख रहा हूँ: हमने उत्पादन से Rx छीन लिया।
जद

13

हास्केल में उच्च-प्रकार के बहुरूपता का सबसे अधिक उपयोग किया जाने वाला उदाहरण Monadइंटरफ़ेस है। Functorऔर Applicativeउसी तरह से उच्चतर हैं, इसलिए मैं Functorकुछ संक्षिप्त दिखाने के लिए दिखाऊंगा।

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

अब, उस परिभाषा की जांच करें, यह देखते हुए कि प्रकार चर fका उपयोग कैसे किया जाता है। आप देखेंगे कि fएक प्रकार का अर्थ नहीं हो सकता है जिसका मूल्य है। आप उस प्रकार के हस्ताक्षर में मानों की पहचान कर सकते हैं क्योंकि वे किसी कार्य के तर्क और परिणाम हैं। तो प्रकार चरa और bप्रकार हैं जो मान हो सकते हैं। तो प्रकार के भाव हैं f aऔर f b। लेकिन fखुद नहीं । fउच्चतर प्रकार के चर का एक उदाहरण है। यह देखते हुए कि *मूल्यों के प्रकार हो सकते हैं, fप्रकार के होने चाहिए * -> *। यही है, यह एक प्रकार लेता है जिसमें मान हो सकते हैं, क्योंकि हम पिछली परीक्षा से जानते हैं aऔर इसमें मान होना चाहिए, इसलिए यह एक प्रकार का रिटर्न देता है जो मान होना चाहिए।b इसमें मान होना चाहिए। और हम यह भी जानते हैं f aऔरf b

यह उच्च-प्रकार के प्रकार की चर fकी परिभाषा में उपयोग किया जाता है Functor

Applicativeऔर Monadइंटरफेस और अधिक जोड़ने, लेकिन वे संगत हैं। इसका मतलब है कि वे प्रकार के साथ-साथ चर पर भी काम करते हैं * -> *

उच्च-प्रकार के प्रकारों पर कार्य करना अमूर्तता के एक अतिरिक्त स्तर का परिचय देता है - आप केवल मूल प्रकारों पर सार बनाने के लिए प्रतिबंधित नहीं हैं। आप उन प्रकारों पर भी सार बना सकते हैं जो अन्य प्रकारों को संशोधित करते हैं।


4
एक और महान तकनीकी व्याख्या जो उच्च प्रकार की है, जो मुझे आश्चर्यचकित करती है कि वे क्या उपयोगी हैं। आपने वास्तविक कोड में इसका लाभ कहां लिया है?
जदयू
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.