क्या मैं हास्केल में एक बहुरूपी फ़ंक्शन के प्रकार को प्रिंट कर सकता हूं जैसे कि अगर मैं इसे एक ठोस प्रकार की इकाई के रूप में पारित कर दूं?

Dec 12 2020

यहाँ 3 प्रकारों में एक कार्य बहुरूपता है:

:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c

और यहाँ एक गैर बहुरूपी कार्य:

:t Data.Char.digitToInt
Data.Char.digitToInt :: Char -> Int

यदि हम पूर्व को उत्तरार्ध में लागू करते हैं, तो हमें 1 प्रकार में एक फ़ंक्शन पॉलीमॉर्फिक मिलता है:

:t (.) Data.Char.digitToInt
(.) Data.Char.digitToInt :: (a -> Char) -> a -> Int

जिसका अर्थ है कि (.)"त्वरित" (मुझे यकीन नहीं है कि यह सही शब्द है; एक सी ++ प्रोग्रामर के रूप में; मैं इसे कॉल करूंगा) b === Charऔर c === Int, इसलिए उस पर हस्ताक्षर (.)किए जाने digitToIntके लिए निम्नलिखित है।

(Char -> Int) -> (a -> Char) -> a -> Int

मेरा प्रश्न है: क्या स्क्रीन पर दिए गए इस हस्ताक्षर को छापने का एक तरीका है, दिया गया है (.), digitToIntऔर "सूचना" जिसे मैं पूर्व को लागू करना चाहता हूं?

किसकी दिलचस्पी है, इस सवाल को पहले इस एक के डुप्लिकेट के रूप में बंद किया गया था ।

जवाब

7 MikeSpivey Dec 13 2020 at 06:32

अन्य उत्तरों को उन कार्यों की सहायता की आवश्यकता होती है जिन्हें कृत्रिम रूप से प्रतिबंधित प्रकारों के साथ परिभाषित किया गया है, जैसे कि asTypeOfHTNW से उत्तर में फ़ंक्शन। यह आवश्यक नहीं है, जैसा कि निम्नलिखित बातचीत से पता चलता है:

Prelude> let asAppliedTo f x = const f (f x)

Prelude> :t head `asAppliedTo` "x"
head `asAppliedTo` "x" :: [Char] -> Char

Prelude> :t (.) `asAppliedTo` Data.Char.digitToInt
(.) `asAppliedTo` Data.Char.digitToInt
  :: (Char -> Int) -> (a -> Char) -> a -> Int

यह लैम्ब्डा-बाइंडिंग में बहुरूपता की कमी का शोषण करता है जिसकी परिभाषा में निहित है asAppliedTofइसके शरीर में होने वाली दोनों घटनाओं को एक ही प्रकार दिया जाना चाहिए, और यह इसके परिणाम का प्रकार है। constयहां उपयोग किए जाने वाले फ़ंक्शन का भी अपना प्राकृतिक प्रकार है a -> b -> a:

const x y = x
12 HTNW Dec 12 2020 at 09:09

इस कोने में छिपा हुआ साफ सुथरा समारोह है Prelude:

Prelude.asTypeOf :: a -> a -> a
asTypeOf x _ = x

इसे "पहले तर्क को दूसरे के समान प्रकार के लिए मजबूर करने" के रूप में प्रलेखित किया गया है। इसका उपयोग हम (.)पहले तर्क के प्रकार को बाध्य करने के लिए कर सकते हैं :

-- (.) = \x -> (.) x = \x -> (.) $ x `asTypeOf` Data.Char.digitToInt -- eta expansion followed by definition of asTypeOf -- the RHS is just (.), but restricted to arguments with the same type as digitToInt -- "what is the type of (.) when the first argument is (of the same type as) digitToInt?" ghci> :t \x -> (.) $ x `asTypeOf` Data.Char.digitToInt
\x -> (.) $ x `asTypeOf` Data.Char.digitToInt
  :: (Char -> Int) -> (a -> Char) -> a -> Int

बेशक, यह उतने ही तर्कों के लिए काम करता है, जितने की जरूरत होती है।

ghci> :t \x y -> (x `asTypeOf` Data.Char.digitToInt) . (y `asTypeOf` head)
\x y -> (x `asTypeOf` Data.Char.digitToInt) . (y `asTypeOf` head)
  :: (Char -> Int) -> ([Char] -> Char) -> [Char] -> Int

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

8 K.A.Buhr Dec 12 2020 at 09:57

मुझे लगता है कि @ HTNW का जवाब शायद इसे कवर करता है, लेकिन पूर्णता के लिए, यहां inContextसमाधान विस्तार से काम करता है।

फ़ंक्शन का प्रकार हस्ताक्षर:

inContext :: a -> (a -> b) -> a

इसका मतलब है कि, यदि आपके पास कोई ऐसी चीज है जिसे आप टाइप करना चाहते हैं, और एक "संदर्भ" जिसमें इसका उपयोग किया जाता है (एक लंब के रूप में अभिव्यक्त किया जाता है जो इसे एक तर्क के रूप में लेता है), प्रकारों के साथ कहें:

thing :: a1
context :: a2 -> b

आप अभिव्यक्ति का निर्माण करके (संदर्भ की बाधाओं ) के साथ ( a1सामान्य प्रकार thing) के एकीकरण को बाध्य कर सकते हैं a2:

thing `inContext` context

आम तौर पर, एकीकृत प्रकार thing :: aखो जाएगा, लेकिन inContextइसका मतलब है कि इस पूरे परिणामी अभिव्यक्ति के प्रकार को भी वांछित प्रकार के साथ एकीकृत किया aजाएगा, और जीएचसीआई खुशी से आपको उस अभिव्यक्ति का प्रकार बताएगा।

तो अभिव्यक्ति:

(.) `inContext` \hole -> hole digitToInt

उस प्रकार (.)को निर्दिष्ट किया जाता है जो निर्दिष्ट संदर्भ में होता है। आप इसे कुछ भ्रामक रूप से लिख सकते हैं, जैसे:

(.) `inContext` \(.) -> (.) digitToInt

चूंकि (.)अनाम लांबा के लिए उतना ही अच्छा तर्क नाम है जितना holeकि। यह संभावित रूप से भ्रामक है, क्योंकि हम एक स्थानीय बंधन बना रहे हैं, जो शीर्ष-स्तरीय परिभाषा को छाया देता है (.), लेकिन यह अभी भी एक ही चीज़ (परिष्कृत प्रकार के साथ) का नामकरण कर रहा है, और लैम्ब्डा के इस दुरुपयोग ने हमें मूल अभिव्यक्ति (.) digitToIntशब्दशः लिखने की अनुमति दी उपयुक्त बॉयलरप्लेट के साथ।

यह वास्तव में अप्रासंगिक है कि कैसे inContextपरिभाषित किया जाता है, यदि आप सिर्फ जीएचसीआई से इसके प्रकार पूछ रहे हैं, तो inContext = undefinedकाम किया होगा। लेकिन, बस प्रकार के हस्ताक्षर को देखते हुए, inContextकाम की परिभाषा देना काफी आसान है :

inContext :: a -> (a -> b) -> a
inContext a _ = a

यह पता चला है कि यह सिर्फ की परिभाषा है const, इसलिए inContext = constकाम भी करता है।

आप inContextएक साथ कई चीजें टाइप करने के लिए उपयोग कर सकते हैं , और वे नामों के बजाय अभिव्यक्ति हो सकते हैं। पूर्व को समायोजित करने के लिए, आप ट्यूपल्स का उपयोग कर सकते हैं; उत्तरार्द्ध काम करने के लिए, आपने अपने लैंब में अधिक समझदार तर्क नामों का उपयोग किया है।

इसलिए, उदाहरण के लिए:

λ> :t (fromJust, fmap length) `inContext` \(a,b) -> a . b
(fromJust, fmap length) `inContext` \(a,b) -> a . b
  :: Foldable t => (Maybe Int -> Int, Maybe (t a) -> Maybe Int)

आपको बताता है कि अभिव्यक्ति में fromJust . fmap length, प्रकारों को विशेष किया गया है:

fromJust :: Maybe Int -> Int
fmap length :: Foldable t => Maybe (t a) -> Maybe Int
6 FyodorSoikin Dec 12 2020 at 03:35

आप TypeApplicationsएक्सटेंशन का उपयोग करके ऐसा कर सकते हैं , जो आपको स्पष्ट रूप से निर्दिष्ट करने की अनुमति देता है कि आप किस प्रकार के मापदंडों का उपयोग करना चाहते हैं:

λ :set -XTypeApplications                                 
λ :t (.) @Char @Int
(.) @Char @Int :: (Char -> Int) -> (a -> Char) -> a -> Int

ध्यान दें कि तर्क सटीक क्रम में होने चाहिए।

ऐसे कार्यों के लिए जिनमें "नियमित" प्रकार के हस्ताक्षर होते हैं foo :: a -> b, जैसे कि उस क्रम से परिभाषित किया जाता है जिसमें प्रकार के पैरामीटर हस्ताक्षर में पहली बार दिखाई देते हैं।

ऐसे कार्यों के लिए जो उपयोग करते ExplicitForallहैं foo :: forall b a. a -> b, यह जो कुछ भी है, उसके द्वारा आदेश को परिभाषित किया जाता है forall


यदि आप विशेष रूप (.)से आवेदन करने के आधार पर टाइप करना चाहते हैं digitToChar(जैसा कि जानने के लिए कि किस प्रकार को भरना है), मुझे पूरा यकीन है कि आप जीएचसीआई में नहीं हो सकते, लेकिन मैं हास्केल आईडीई समर्थन की अत्यधिक अनुशंसा कर सकता हूं।

उदाहरण के लिए, यहाँ VSCode (यहाँ विस्तार है ) में यह मेरे लिए कैसा दिखता है :

5 chi Dec 12 2020 at 16:39

यह HTNW के उत्तर पर एक मामूली बदलाव है।

मान लीजिए कि हमारे पास कोई भी, संभावित रूप से बड़ी, अभिव्यक्ति है जिसमें एक बहुरूपी पहचानकर्ता शामिल है poly

 .... poly ....

और हमें आश्चर्य है कि उस बिंदु पर पॉलीमॉर्फिक प्रकार को कैसे त्वरित किया गया था।

यह GHC की दो विशेषताओं का शोषण किया जा सकता है: asTypeOf(HTNW द्वारा उल्लिखित) और टाइप किए गए छेद , निम्नानुसार हैं:

 .... (poly `asTypeOf` _) ....

_छेद को पढ़ने पर , जीएचसी उस छेद के स्थान पर दर्ज किए जाने वाले शब्द के प्रकार की रिपोर्ट करने में त्रुटि उत्पन्न करेगा। चूंकि हमने उपयोग किया था asTypeOf, यह उसी प्रकार का होना चाहिए, जिस प्रकार के polyउस संदर्भ में हमें जिस विशेष उदाहरण की आवश्यकता है।

यहाँ GHCi में एक उदाहरण है:

> ((.) `asTypeOf` _) Data.Char.digitToInt
<interactive>:11:17: error:
    * Found hole: _ :: (Char -> Int) -> (a -> Char) -> a -> Int