प्रकार चेकर बहुत गलत प्रकार के प्रतिस्थापन की अनुमति दे रहा है, और कार्यक्रम अभी भी संकलित है


99

मेरे कार्यक्रम में एक समस्या को डीबग करने की कोशिश करते हुए (ग्लोस का उपयोग करके अलग-अलग आकार के साथ 2 सर्कल अलग-अलग आकारों में तैयार किए जा रहे हैं *), मैं एक अजीब स्थिति में ठोकर खाई। ऑब्जेक्ट्स को हैंडल करने वाली मेरी फ़ाइल में, मेरे पास निम्न के लिए परिभाषा है Player:

type Coord = (Float,Float)
data Obj =  Player  { oPos :: Coord, oDims :: Coord }

और मेरी मुख्य फ़ाइल में, जो कि Objects.hs को आयात करता है, मेरी निम्न परिभाषा है:

startPlayer :: Obj
startPlayer = Player (0,0) 10

यह मेरे लिए खिलाड़ी को जोड़ने और बदलने के क्षेत्रों के कारण हुआ, और startPlayerबाद में अपडेट करने के लिए भूल गया (इसके आयाम एक त्रिज्या का प्रतिनिधित्व करने के लिए एक ही संख्या द्वारा निर्धारित किए गए थे, लेकिन मैंने इसे Coordकभी भी प्रतिनिधित्व करने के लिए बदल दिया (चौड़ाई, ऊंचाई); खिलाड़ी एक गैर-सर्कल वस्तु)।

आश्चर्यजनक बात यह है कि उपरोक्त कोड गलत प्रकार का दूसरा क्षेत्र होने के बावजूद संकलित करता है, और चलता है।

मैंने पहले सोचा था कि शायद मेरे पास फ़ाइलों के अलग-अलग संस्करण खुले हैं, लेकिन संकलित कार्यक्रम में किसी भी फाइल में कोई भी परिवर्तन दिखाई दिया।

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

मैं अपने स्वयं के फ़ाइल में ऊपर 2 के टुकड़े चिपकाने की कोशिश की, और यह उम्मीद त्रुटि है कि के दूसरे क्षेत्र से बाहर थूका Playerमें startPlayerसही नहीं है।

संभवतः ऐसा क्या हो सकता है? आपको लगता है कि यह बहुत अच्छी बात है कि हास्केल के टाइप चेकर को रोकना चाहिए।


* मेरी मूल समस्या का उत्तर, अलग-अलग आकार के दो त्रिज्याओं को समान रूप से खींचा जाना, यह था कि एक त्रिज्या वास्तव में नकारात्मक थी।


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

1
किया हुआ। क्या उदाहरणों को बाहर करना संभव है? पुस्तकालय में कार्य करने के लिए उन्हें इसकी आवश्यकता हो सकती है, लेकिन मुझे इसकी आवश्यकता नहीं है। मैंने यह भी देखा कि उन्होंने न्यूम कलर को परिभाषित किया था। यह केवल समय की बात है इससे पहले कि मुझे झांसा दिया।
कारजेनिकेट

@ अच्छी तरह से, बहुत देर हो चुकी है। और मैंने इसे केवल एक या एक सप्ताह पहले डाउनलोड किया, एक अद्यतन अप-टू-डेट कैबल का उपयोग करके; इसलिए यह चालू होना चाहिए।
कारकेजेनिकेट

2
@ChristianConkle एक मौका है कि लेखक के लेखक को यह समझ में नहीं आया कि TypeSynonymInstances क्या करता है। किसी भी मामले में, यह वास्तव में दूर जाना (या तो करने की जरूरत Pointएक newtypeया आला अन्य ऑपरेटर नाम का उपयोग linear)
घन

1
@Cubic: TypeSynonymInstances अपने दम पर बुरा नहीं है (हालाँकि पूरी तरह से हानिरहित नहीं है), लेकिन जब आप इसे ओवरलैपिंग के साथ जोड़ते हैं तो चीजें बहुत मजेदार हो जाती हैं।
जॉन एल

जवाबों:


128

एकमात्र तरीका यह संभवतः संकलित कर सकता है यदि कोई Num (Float,Float)उदाहरण मौजूद है । यह मानक पुस्तकालय द्वारा प्रदान नहीं किया गया है, हालांकि यह संभव है कि जिन पुस्तकालयों का आप उपयोग कर रहे हैं, उनमें से एक इसे किसी पागल कारण से जोड़ा जाए। अपनी परियोजना को ghci में लोड करने का प्रयास करें और देखें कि क्या 10 :: (Float,Float)काम करता है, तो :i Numयह पता लगाने की कोशिश करें कि उदाहरण कहां से आ रहा है, और फिर इसे परिभाषित करें।

परिशिष्ट: उदाहरणों को बंद करने का कोई तरीका नहीं है। मॉड्यूल से उन्हें निर्यात नहीं करने का एक तरीका भी नहीं है । यदि यह संभव थे, तो यह और भी करने के लिए नेतृत्व करेंगे अधिक कोड भ्रमित। यहां एकमात्र वास्तविक समाधान इस तरह के उदाहरणों को परिभाषित नहीं करना है।


53
वाह। 10 :: (Float, Float)पैदावार होती है (10.0,10.0), और :i Numइसमें रेखा होती है instance Num Point -- Defined in ‘Graphics.Gloss.Data.Point’( Pointकोऑर्ड का ग्लोस उर्फ)। गंभीरता से? धन्यवाद। उस रात मुझे नींद से बचाया।
कार्निजेन्टिक

6
@Carcigenicate जबकि ऐसे उदाहरणों की अनुमति देना टेढ़ी खीर लगता है, इसका कारण यह है कि अनुमति दी गई है ताकि डेवलपर्स अपने स्वयं के उदाहरण लिख सकें, Numजहां यह समझ में आता है, जैसे कि एक Angleडेटा प्रकार जो एक और के Doubleबीच बाधा डालता है , या यदि कोई डेटा प्रकार लिखना चाहता था। चतुर्धातुक या कुछ और अधिक जटिल संख्यात्मक प्रकार का प्रतिनिधित्व करना यह सुविधा बहुत सुविधाजनक है। यह भी / / के समान नियमों का पालन करता है , इन उदाहरणों को आसानी से उपयोग करने के दृष्टिकोण से समझ में आता है, लेकिन इस मामले में इसका दुरुपयोग किया जा सकता है। -pipiStringTextByteString
भीलकिरड़

4
@bheklilr मैं संख्या के उदाहरणों की अनुमति देने की आवश्यकता को समझता हूं। कुछ चीजों से उपजी "वाह"। मुझे नहीं पता था कि आप टाइप उपनामों के उदाहरण बना सकते हैं, एक कॉर्ड का एक उदाहरण बनाकर बस काउंटर सहज ज्ञान युक्त लगता है, और मैंने ऐसा नहीं सोचा था। ओह ठीक है, सबक सीखा।
कार्कजेनिकेट

3
आप अपनी लाइब्रेरी के अनाथ उदाहरण के साथ अपनी समस्या को हल कर सकते हैं बजाय एक के newtypeलिए एक घोषणा का उपयोग करके । Coordtype
बेंजामिन हॉजसन

3
@Carcigenicate मेरा मानना ​​है कि आपको टाइप-समानार्थक शब्दों के लिए उदाहरणों की अनुमति देने के लिए -XTypeSynonymInstances की जरूरत है , लेकिन समस्यात्मक उदाहरण बनाने के लिए यह आवश्यक नहीं है। उदाहरण के लिए Num (Float, Float)या यहां तक (Floating a) => Num (a,a)कि विस्तार की आवश्यकता नहीं होगी, लेकिन उसी व्यवहार के परिणामस्वरूप होगा।
क्रोकेईए

64

हास्केल का प्रकार चेकर उचित है। समस्या यह है कि आपके द्वारा उपयोग किए जा रहे पुस्तकालय के लेखकों ने कुछ किया है ... कम उचित।

संक्षिप्त उत्तर है: हाँ, 10 :: (Float, Float)पूरी तरह से वैध है यदि कोई उदाहरण है Num (Float, Float)। संकलक या भाषा के दृष्टिकोण से इसके बारे में "बहुत गलत" कुछ भी नहीं है। यह सिर्फ हमारे अंतर्ज्ञान के साथ वर्ग नहीं करता है कि संख्यात्मक अंक क्या करते हैं। जब से आप टाइप की गई प्रणाली को पकड़ते हैं, तो आप जिस तरह की त्रुटि करते हैं, आप हैरान और निराश होते हैं!

Numउदाहरण और fromIntegerसमस्या

आप हैरान हैं कि कंपाइलर स्वीकार करता है 10 :: Coord, यानी 10 :: (Float, Float)। यह मान लेना उचित है कि जैसे संख्यात्मक शाब्दिक 10"सांख्यिक" प्रकार के लिए अनुमानित होंगे। बॉक्स से बाहर, संख्यात्मक शाब्दिक व्याख्या की जा सकती रूप में Int, Integer, Float, या Double। किसी अन्य संदर्भ के साथ संख्याओं का एक प्रकार, उन चार प्रकारों की संख्याओं के तरीके की तरह नहीं लगता है। हम बात नहीं कर रहे हैं Complex

सौभाग्य से या दुर्भाग्य से, हालांकि, हास्केल एक बहुत ही लचीली भाषा है। मानक निर्दिष्ट करता है कि एक पूर्णांक शाब्दिक की 10व्याख्या की जाएगी fromInteger 10, जिसका प्रकार है Num a => a। तो किसी भी प्रकार के बारे में 10अनुमान लगाया जा सकता है कि इसके लिए एक उदाहरण लिखा गया था। मैं इसे एक और जवाब में थोड़ा और विस्तार से समझाता हूं ।Num

इसलिए जब आप अपना प्रश्न पोस्ट करते हैं, तो एक अनुभवी हास्केलर तुरंत स्पॉट करता है कि 10 :: (Float, Float)स्वीकार किए जाने के लिए, एक उदाहरण होना चाहिए जैसे Num a => Num (a, a)या Num (Float, Float)। इसमें ऐसा कोई उदाहरण नहीं है Prelude, इसलिए इसे कहीं और परिभाषित किया गया होगा। उपयोग करते हुए :i Num, आप जल्दी से स्पॉट हुए कि यह कहाँ से आया है: glossपैकेज।

टाइप पर्यायवाची और अनाथ उदाहरण

लेकिन एक मिनट पकड़ो। आप glossइस उदाहरण में किसी भी प्रकार का उपयोग नहीं कर रहे हैं ; glossआपको प्रभावित करने का उदाहरण क्यों दिया गया ? जवाब दो चरणों में आता है।

सबसे पहले, कीवर्ड के साथ पेश किया गया एक प्रकार का पर्याय typeएक नया प्रकार नहीं बनाता है । आपके मॉड्यूल में, लेखन Coordकेवल इसके लिए शॉर्टहैंड है (Float, Float)। इसी तरह Graphics.Gloss.Data.Point, Pointसाधन में (Float, Float)। दूसरे शब्दों में, अपने Coordऔर glossकी Pointसचमुच बराबर हैं।

इसलिए जब glossअनुरक्षकों ने लिखना चुना instance Num Point where ..., तो उन्होंने आपके Coordप्रकार को भी एक उदाहरण बना दिया Num। के बराबर instance Num (Float, Float) where ...या है instance Num Coord where ...

(डिफ़ॉल्ट रूप से, हास्केल प्रकार के समानार्थक शब्द को वर्ग उदाहरण नहीं बनने देते हैं। glossलेखकों को भाषा एक्सटेंशन की एक जोड़ी को सक्षम करना था, TypeSynonymInstancesऔर FlexibleInstances, उदाहरण के लिए लिखना होगा।)

दूसरा, यह आश्चर्य की बात है क्योंकि यह एक अनाथ उदाहरण है , अर्थात एक उदाहरण घोषणा instance C Aजहां दोनों Cऔर Aअन्य मॉड्यूल में परिभाषित किए गए हैं। यहाँ यह क्योंकि प्रत्येक भाग शामिल विशेष रूप से घातक है, यानी Num, (,)है, और Float, से आता है Preludeऔर हर जगह दायरे में रहने की संभावना है।

आपकी अपेक्षा है कि Numपरिभाषित किया गया है Preludeऔर tuples और Floatमें परिभाषित कर रहे हैं Prelude, तो उन तीन चीजों के बारे में सब कुछ कैसे काम में परिभाषित किया गया है Prelude। आयात करने से पूरी तरह से अलग मॉड्यूल कुछ भी क्यों बदल जाएगा? आदर्श रूप से यह नहीं होगा, लेकिन अनाथ उदाहरण उस अंतर्ज्ञान को तोड़ते हैं।

(ध्यान दें कि GHC अनाथ उदाहरणों के बारे में चेतावनी देता है - glossविशेष रूप से उस चेतावनी को ओवररोड करने वाले लेखक । यह लाल झंडा उठाया जाना चाहिए और प्रलेखन में कम से कम एक चेतावनी दी गई है।)

कक्षा के उदाहरण वैश्विक हैं और इन्हें छिपाया नहीं जा सकता है

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

आप आयात सूचियों से बचने के लिए आयात सूचियों का उपयोग नहीं कर सकते । इसी तरह, आप उन मॉड्यूल से निर्यात करने से बच नहीं सकते जिन्हें आप परिभाषित करते हैं।

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

(वैसे, जैसा कि इस उत्तर ने प्रदर्शित किया है , आप अनाथ उदाहरणों का उपयोग करके कुछ मामलों में वैश्विक-उदाहरण धारणा को तोड़ सकते हैं !)

क्या करना है - पुस्तकालय कार्यान्वयनकर्ताओं के लिए

लागू करने से पहले दो बार सोचें Num। आप के आसपास काम नहीं कर सकता fromInteger-कोई समस्या नहीं, को परिभाषित fromInteger = error "not implemented"करता है नहीं इसे बेहतर बनाने। क्या आपके उपयोगकर्ता भ्रमित या आश्चर्यचकित होंगे-या इससे भी बदतर, कभी नोटिस नहीं करेंगे - यदि उनके पूर्णांक शाब्दिक गलती से आपके द्वारा टाइप किए जाने वाले टाइप के लिए अनुमानित हैं? प्रदान कर रहा है (*)और (+)यह महत्वपूर्ण है — खासकर यदि आपको इसे हैक करना है?

Conal Elliott's vector-space(प्रकार के प्रकार *) या Edward Kmett linear(प्रकार के प्रकार * -> *) के लिए एक पुस्तकालय में परिभाषित वैकल्पिक अंकगणितीय ऑपरेटरों का उपयोग करने पर विचार करें । यह वही है जो मैं खुद करता हूं।

का उपयोग करें -Wall। अनाथ उदाहरणों को लागू न करें, और अनाथ उदाहरण चेतावनी को अक्षम न करें।

वैकल्पिक रूप से, linearऔर कई अन्य अच्छी तरह से व्यवहार किए गए पुस्तकालयों के नेतृत्व का पालन करें , और एक अलग मॉड्यूल में .OrphanInstancesया अनाथ उदाहरणों को प्रदान करें .Instances। और उस मॉड्यूल को किसी अन्य मॉड्यूल से आयात न करें । तब उपयोगकर्ता चाहें तो अनाथों को स्पष्ट रूप से आयात कर सकते हैं।

यदि आप अपने आप को अनाथों को परिभाषित करते हुए पाते हैं, तो संभव है और उचित होने पर, अपस्ट्रीम अनुरक्षकों को उन्हें लागू करने के लिए कहें। मैं अक्सर अनाथ उदाहरण लिखता था Show a => Show (Identity a), जब तक कि उन्होंने इसे नहीं जोड़ा transformers। मैंने इसके बारे में एक बग रिपोर्ट भी उठाई होगी; मुझे याद नहीं है।

क्या करना है - पुस्तकालय उपभोक्ताओं के लिए

आपके पास कई विकल्प नहीं हैं। लाइब्रेरी तक पहुंचने के लिए विनम्रतापूर्वक और रचनात्मक रूप से पहुंचें! उन्हें इस सवाल की ओर इशारा करें। समस्याग्रस्त अनाथ को लिखने के लिए उनके पास कुछ विशेष कारण हो सकते हैं, या वे सिर्फ महसूस नहीं कर सकते हैं।

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

पर्यायवाची के newtypeबजाय अपने स्वयं के परिभाषित करें typeयदि यह पर्याप्त महत्वपूर्ण है। आपको पूरा यकीन है कि कोई भी उनके साथ गड़बड़ नहीं करेगा।

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.