2013-07-25 11 views
16

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

+2

ये जवाब हैं आप सभी महान धन्यवाद everone! –

उत्तर

13

मुझे विश्वास है कि उत्तर एक योग्य हां है, जो आप प्राप्त करने की कोशिश कर रहे हैं उसके आधार पर।

आप टाइप क्लास नामों के नामों को निर्यात करते समय टाइप इंटरफ़ेस मॉड्यूल से टाइप क्लास नाम को निर्यात करने से बच सकते हैं। तब कोई भी कक्षा का उदाहरण नहीं दे सकता क्योंकि कोई भी इसका नाम नहीं दे सकता!

उदाहरण:

module Foo (
    foo, 
    bar 
) where 

class SecretClass a where 
    foo :: a 
    bar :: a -> a -> a 

instance SecretClass Int where 
    foo = 3 
    bar = (+) 

नकारात्मक पक्ष यह है कि कोई भी या तो एक बाधा के रूप में अपने वर्ग के साथ एक प्रकार लिख सकते हैं। यह पूरी तरह से लोगों को उन कार्यों को लिखने से रोकता है जिनके पास ऐसा प्रकार होगा, क्योंकि संकलक अभी भी अनुमान प्रकार में सक्षम होगा। लेकिन यह बहुत परेशान होगा।

आप एक सुपर-क्लास के रूप में अपनी "बंद" कक्षा के साथ, एक और खाली प्रकार वर्ग प्रदान करके नकारात्मक पक्ष को कम कर सकते हैं। आप अपनी मूल कक्षा के प्रत्येक उदाहरण को उप-वर्ग का एक उदाहरण भी बनाते हैं, और आप उप वर्ग (सभी प्रकार के वर्ग कार्यों के साथ) निर्यात करते हैं, लेकिन सुपर क्लास नहीं। (स्पष्टता के लिए आपको अपने द्वारा खुलासा किए जाने वाले सभी प्रकारों में "गुप्त" की बजाय "सार्वजनिक" वर्ग का उपयोग करना चाहिए, लेकिन मेरा मानना ​​है कि यह किसी भी तरह से काम करता है)।

उदाहरण:

{-# LANGUAGE FlexibleInstances, UndecidableInstances #-} 

module Foo ( 
    PublicClass, 
    foo, 
    bar 
) where 

class SecretClass a where 
    foo :: a 
    bar :: a -> a -> a 

class SecretClass a => PublicClass a 

instance SecretClass Int where 
    foo = 3 
    bar = (+) 

instance SecretClass a => PublicClass a 

आप एक्सटेंशन के बिना कर सकते हैं यदि आप मैन्युअल SecretClass के प्रत्येक उदाहरण के लिए PublicClass का एक उदाहरण घोषित करने के लिए तैयार हैं।

अब ग्राहक कोड PublicClass का उपयोग प्रकार वर्ग की कमी लिखने के लिए कर सकते हैं, लेकिन PublicClass के प्रत्येक उदाहरण एक ही प्रकार के लिए SecretClass का एक उदाहरण आवश्यकता है, और कोई रास्ता नहीं के साथ SecretClass का एक नया उदाहरण घोषित करने के लिए कोई भी किसी भी अधिक प्रकार के उदाहरणों कर सकते हैं PublicClass का।

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


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

मुझे लगता है कि एक्सटेंशन के साथ कोई नया ओवरलैपिंग उदाहरण घोषित कर सकता है। जैसे अगर आपने [a] के लिए एक उदाहरण प्रदान किया है, तो PublicClass का [Int] के लिए कोई नया उदाहरण घोषित कर सकता है, जो SecretClass के उदाहरण पर [a] के उदाहरण पर पिगबैक करेगा।लेकिन दिया गया है कि PublicClass में कोई फ़ंक्शन नहीं है और वे SecretClass का एक उदाहरण नहीं लिख सकते हैं, मैं नहीं देख सकता कि इसके साथ बहुत कुछ किया जा सकता है।

+0

हालांकि कोई भी अभी भी 'पब्लिक क्लास' के उदाहरण बना सकता है, और जब मुझे नहीं पता कि कौन सी टेलिसी हासिल करने की कोशिश कर रहा था, तो मैं अभी इस परिदृश्य के बारे में सोच नहीं सकता हूं, जहां इसका 'सिरेक्ट क्लास' निर्यात करने के समान प्रभाव नहीं होंगे। पहले स्थान पर। क्या आप? – firefrorefiddle

+1

@ माइक हार्टल नहीं, आप उन प्रकार के लिए 'पब्लिक क्लास' के उदाहरण नहीं बना सकते हैं जो पहले से ही 'गुप्त क्लास' के उदाहरण नहीं हैं। आपको एक इंस्टेंस घोषणा के सुपरक्लास से उत्पन्न होने वाली कोई घटना नहीं है (Foo.SecretClass Bool) संभावित फिक्स: (Foo.SecretClass Bool) के लिए एक उदाहरण घोषणा जोड़ें। और आप उस उदाहरण को जोड़ नहीं सकते हैं, क्योंकि 'SecretClass' मॉड्यूल के बाहर कहीं भी दायरे में नहीं है। – Ben

+0

आह! मेरी गलती। मैंने इसे 'इंस्टेंस (SecretClass ए) => पब्लिक क्लास ए' के ​​साथ गलत समझा। – firefrorefiddle

18

GHC 7.8.1, closed type families के बाद घोषित किया जा सकता है, और मैं उन्हें की मदद से लगता है, और ConstraintKinds, तो आप ऐसा कर सकते हैं:

type family SecretClass (a :: *) :: Constraint where 
    SecretClass Int =() 

SecretClass a की कोई समस्या है, एक प्रकार वर्ग के बराबर रूपों, और चूंकि परिवार को किसी के द्वारा विस्तारित नहीं किया जा सकता है, इसलिए "वर्ग" के किसी अन्य उदाहरण को परिभाषित नहीं किया जा सकता है।

(यह वास्तव में, बस अटकलें के बाद से मैं यह परीक्षण नहीं कर सकते, लेकिन this interesting link में कोड यह देखो की तरह यह काम करेगा बनाता है।)

+1

साफ विचार, धन्यवाद। –

+1

पैट्रिक बह्र के पेपर के दोनों लिंक 404 हैं। बहरी की साइट से [यह लिंक] (http://bahr.io/pubs/files/serrano15haskell-paper.pdf) आज़माएं। –

7

आप एक डेटा घोषणा में typeclass refactor सकता है (उपयोग रिकॉर्ड वाक्य रचना) जिसमें आपके टाइपक्लास के सभी फ़ंक्शन शामिल हैं। उदाहरणों की एक निश्चित सीमित सूची आपको लगता है कि आपको किसी भी वर्ग की आवश्यकता नहीं है।

यह निश्चित रूप से यह है कि संकलक वैसे भी आपके वर्ग के दृश्यों को व्यवहार कर रहा है।

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

यह ठीक काम करता है क्योंकि डेटा प्रकार मॉड्यूल-सीमा-पार करने वाली खुली दुनिया धारणा के अधीन नहीं हैं टाइपक्लास हैं।

कभी-कभी टाइप सिस्टम जटिलता जोड़ना चीजों को कठिन बनाता है।

11

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

ध्यान दें कि यह दृष्टिकोण मॉड्यूल सिस्टम से स्वतंत्र है। मॉड्यूल सीमाओं पर भरोसा करने के बजाय, हम एक स्पष्ट सूची प्रदान करते हैं कि कौन से उदाहरण कानूनी हैं। इसका मतलब यह है कि, एक तरफ, कानूनी उदाहरण कई मॉड्यूल या यहां तक ​​कि संकुलों पर फैल सकते हैं, और दूसरी ओर, हम एक ही मॉड्यूल में अवैध उदाहरण प्रदान नहीं कर सकते हैं।

इस जवाब के लिए, मुझे लगता है कि हम निम्नलिखित वर्ग को बंद करना चाहते हैं ताकि इसमें प्रकार Int और Integer के लिए instantiated जा सकता है लेकिन अन्य प्रकार के लिए नहीं:

-- not yet closed 
class Example a where 
    method :: a -> a 

सबसे पहले, हम एक की जरूरत है बंद प्रकार परिवारों को एन्कोडिंग के लिए छोटे प्रकार के ढांचे को संबंधित प्रकार के परिवारों के रूप में।

{-# LANGUAGE TypeFamilies, EmptyDataDecls #-} 

class Closed c where 
    type Instance c a 

पैरामीटर c प्रकार परिवार और पैरामीटर a के नाम के लिए खड़ा प्रकार परिवार का सूचकांक है। c का परिवार उदाहरण a के लिए Instance c a के रूप में एन्कोड किया गया है। चूंकि c एक वर्ग पैरामीटर भी है, इसलिए c के सभी पारिवारिक उदाहरणों को एक वर्ग के उदाहरण घोषणा में एक साथ दिया जाना है।

अब, हम एन्कोड करने के लिए कि Int और IntegerOk हैं, और अन्य सभी प्रकार के नहीं हैं एक बंद प्रकार परिवार MemberOfExample परिभाषित करने के लिए इस ढांचे का उपयोग करें।

data MemberOfExample 
data Ok 

instance Closed MemberOfExample where 
    type Instance MemberOfExample Int = Ok 
    type Instance MemberOfExample Integer = Ok 

अंत में, हम हमारे Example की एक सुपर क्लास contraint में इस बंद प्रकार परिवार का उपयोग करें।

class Instance MemberOfExample a ~ Ok => Example a where 
    method :: a -> a 

हम Int और Integer हमेशा की तरह के लिए मान्य उदाहरणों परिभाषित कर सकते हैं।

instance Example Int where 
    method x = x + 1 

instance Example Integer where 
    method x = x + 1 

लेकिन हम Int और Integer के अलावा अन्य प्रकार के लिए अमान्य उदाहरणों को परिभाषित नहीं कर सकते हैं।

-- GHC error: Couldn't match type `Instance MemberOfExample Float' with `Ok' 
instance Example Float where 
    method x = x + 1 

और हम वैध प्रकारों के सेट को भी विस्तारित नहीं कर सकते हैं।

-- GHC error: Duplicate instance declarations 
instance Closed MemberOfExample where 
    type Instance MemberOfExample Float = Ok 

-- GHC error: Associated type `Instance' must be inside a class instance 
type instance Instance MemberOfExample Float = Ok 

दुर्भाग्यवश, हम निम्न फर्जी उदाहरण लिख सकते हैं:

-- Unfortunately accepted 
instance Instance MemberOfExample Float ~ Ok => Example Float where 
    method x = x + 1 

लेकिन चूंकि हम समानता बाधा का निर्वहन करने में सक्षम नहीं होगा, मुझे नहीं लगता कि हम कभी भी कुछ भी करने के लिए उपयोग कर सकते हैं करते हैं। उदाहरण के लिए, निम्न को अस्वीकार कर दिया गया है:

-- Couldn't match type `Instance MemberOfExample Float' with `Ok' 
test = method (pi :: Float) 
+0

यह @ बेन के उत्तर की तरह बहुत प्रतीत होता है। क्या आप विस्तार से बता सकते हैं कि वे कैसे भिन्न हैं? –

+0

@tel: सबसे महत्वपूर्ण अंतर यह प्रतीत होता है कि यह दृष्टिकोण मॉड्यूल सिस्टम से स्वतंत्र है। मैंने इसे इंगित करने के उत्तर में एक पैराग्राफ जोड़ा। – Toxaris

1

जब सब आपको लगता है कि आप में रुचि रखते उदाहरणों में से एक enumertated सेट कर रहे हैं, तो यह चाल मदद कर सकता है:

class (Elem t '[Int, Integer, Bool] ~ True) => Closed t where 

type family Elem (t :: k) (ts :: [k]) :: Bool where 
    Elem a '[] = False 
    Elem a (a ': as) = True 
    Elem a (b ': bs) = Elem a bs 

instance Closed Int 
instance Closed Integer 
-- instance Closed Float -- ERROR 
संबंधित मुद्दे