2015-04-17 11 views
10

क्या मैं संकलक को समझ सकता हूं कि एक बाधा हमेशा बंद प्रकार के परिवार में समानार्थी शब्द से संतुष्ट होती है? परिवार को प्रचारित मूल्यों के एक सीमित सेट द्वारा अनुक्रमित किया जाता है।प्रतिबंधित बंद प्रकार परिवार

data NoShow = NoShow 
data LiftedType = V1 | V2 | V3 

type family (Show (Synonym (a :: LiftedType)) => Synonym (a :: LiftedType)) where 
    Synonym V1 = Int 
    Synonym V2 = NoShow -- no Show instance => compilation error 
    Synonym V3 =() 

की तर्ज पर

कुछ मैं खुले प्रकार परिवारों पर एक बाधा को लागू कर सकते हैं:

class (Show (Synonym a)) => SynonymClass (a :: LiftedType) where 
    type Synonym a 
    type Synonym a =() 

instance SynonymClass Int where 
    type Synonym V1 = Int 

-- the compiler complains here 
instance SynonymClass V2 where 
    type Synonym V2 = NoShow 

instance SynonymClass V3 

लेकिन संकलक तो होता तथ्य मौजूद है के बारे में तर्क करने में सक्षम होने की V1, V2 और V3 के प्रत्येक के लिए SynonymClass a का एक उदाहरण? लेकिन किसी भी मामले में, मैं एक खुले प्रकार के परिवार का उपयोग नहीं करना पसंद करूंगा।

इसकी आवश्यकता के लिए मेरी प्रेरणा यह है कि मैं संकलक को मनाने के लिए चाहता हूं कि मेरे कोड में एक बंद प्रकार के परिवार के सभी उदाहरणों को दिखाएं/पढ़ें उदाहरण हैं। एक सरलीकृत उदाहरण है:

parseLTandSynonym :: LiftedType -> String -> String 
parseLTandSynonym lt x = 
    case (toSing lt) of 
    SomeSing (slt :: SLiftedType lt') -> parseSynonym slt x 

parseSynonym :: forall lt. SLiftedType lt -> String -> String 
parseSynonym slt flv = 
     case (readEither flv :: Either String (Synonym lt)) of 
     Left err -> "Can't parse synonym: " ++ err 
     Right x -> "Synonym value: " ++ show x 

[किसी ने टिप्पणी है कि यह संभव नहीं है में उल्लेख किया है - यह है क्योंकि यह तकनीकी रूप से असंभव है है (और यदि हां, तो क्यों) या सिर्फ GHC कार्यान्वयन की एक सीमा?]

+0

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

+1

क्यों न सिर्फ 'parseF :: forall lt। (पढ़ें (समानार्थी शब्द), दिखाएँ (समानार्थी शब्द)) => स्लीफ्टेड टाइप लेफ्टिनेंट -> स्ट्रिंग -> स्ट्रिंग'? यह आपके उद्देश्यों के लिए पर्याप्त है, क्योंकि मैं इसे समझता हूं। –

+0

@ AndrásKovács मैंने अपने प्रेरक उदाहरण में कुछ और संदर्भ जोड़ा है। 'स्लिफ्टेड टाइप लेफ्टिन'' का मान सामने नहीं है - मैं '' (स्ट्रिंग, स्ट्रिंग) '' '' '(लिफ्टेड टाइप, स्ट्रिंग)' 'और फिर' '(स्लिफ्टेड टाइप लेफ्टिनेंट, समानार्थी lt) '', लेकिन 'कुछ' 'केस स्टेटमेंट में निर्भर रूप से टाइप किए गए भाग को छुपाएं। – dbeacham

उत्तर

4

समस्या यह है कि हम उदाहरण के सिर में Synonym नहीं डाल सकते क्योंकि यह एक प्रकार का परिवार है, और हम (forall x. Show (Synonym x)) => ... जैसे "सार्वभौमिक रूप से मात्राबद्ध" बाधा नहीं लिख सकते हैं क्योंकि हास्केल में ऐसी कोई चीज़ नहीं है। (exists x. f x) -> a

  • singletons के defunctionalization को

    • forall x. f x -> a बराबर है हमें उदाहरण सिर में प्रकार परिवारों वैसे भी डाल देता है:

      हालांकि, हम दो चीजों का उपयोग कर सकते हैं।

      data Some :: (TyFun k * -> *) -> * where 
          Some :: Sing x -> f @@ x -> Some f 
      

      और हम भी LiftedType के लिए defunctionalization प्रतीक शामिल हैं:: अब

      import Data.Singletons.TH 
      import Text.Read 
      import Control.Applicative 
      
      $(singletons [d| data LiftedType = V1 | V2 | V3 deriving (Eq, Show) |]) 
      
      type family Synonym t where 
          Synonym V1 = Int 
          Synonym V2 =() 
          Synonym V3 = Char 
      
      data SynonymS :: TyFun LiftedType * -> * -- the symbol for Synonym 
      type instance Apply SynonymS t = Synonym t 
      

      , हम कर सकते हैं

    तो, हम एक अस्तित्व आवरण कि singletons शैली प्रकार कार्यों पर काम करता है परिभाषित के बजाय Some SynonymS -> a का उपयोग करें, और इस फ़ॉर्म का उपयोग उदाहरणों में भी किया जा सकता है।

    instance Show (Some SynonymS) where 
        show (Some SV1 x) = show x 
        show (Some SV2 x) = show x 
        show (Some SV3 x) = show x 
    
    instance Read (Some SynonymS) where 
        readPrec = undefined -- I don't bother with this now... 
    
    parseLTandSynonym :: LiftedType -> String -> String 
    parseLTandSynonym lt x = 
        case (toSing lt) of 
        SomeSing (slt :: SLiftedType lt') -> parseSynonym slt x 
    
    parseSynonym :: forall lt. SLiftedType lt -> String -> String 
    parseSynonym slt flv = 
         case (readEither flv :: Either String (Some SynonymS)) of 
         Left err -> "Can't parse synonym: " ++ err 
         Right x -> "Synonym value: " ++ show x 
    

    यह सीधे हमें t के किसी भी विशिष्ट चुनाव के लिए Read (Synonym t) प्रदान नहीं करता है, हालांकि हम अभी भी अस्तित्व टैग पर Some SynonymS और फिर पैटर्न मैच पढ़ अगर हम सही प्रकार मिल जाँच करने के लिए कर सकते हैं (और असफल हो ऐसा नहीं है सही)। यह read के सभी उपयोग मामलों को बहुत अधिक कवर करता है।

    यदि यह पर्याप्त नहीं है, तो हम एक और रैपर का उपयोग कर सकते हैं, और "सार्वभौमिक रूप से प्रमाणित" उदाहरणों के लिए Some f उदाहरण उठा सकते हैं।

    data At :: (TyFun k * -> *) -> k -> * where 
        At :: Sing x -> f @@ x -> At f x 
    

    At f xf @@ x के बराबर है, लेकिन हम इसके लिए उदाहरणों लिख सकते हैं।विशेष रूप से, हम यहां एक समझदार सार्वभौमिक Read उदाहरण लिख सकते हैं।

    instance (Read (Some f), SDecide (KindOf x), SingKind (KindOf x), SingI x) => 
        Read (At f x) where 
        readPrec = do 
         Some tag x <- readPrec :: ReadPrec (Some f) 
         case tag %~ (sing :: Sing x) of 
         Proved Refl -> pure (At tag x) 
         Disproved _ -> empty 
    

    हम पहले एक Some f पार्स, तो जाँच करें कि क्या पार्स प्रकार सूचकांक सूचकांक पार्स करने में हम चाहते हैं के बराबर है। यह विशिष्ट सूचकांक वाले पार्सिंग प्रकारों के लिए ऊपर वर्णित पैटर्न का एक अमूर्त है। यह अधिक सुविधाजनक है क्योंकि हमारे पास At पर पैटर्न मिलान में केवल एक ही मामला है, इससे कोई फर्क नहीं पड़ता कि हमारे पास कितने सूचकांक हैं। नोटिस हालांकि SDecide बाधा। यह %~ विधि प्रदान करता है, और singletons हमारे लिए यह प्राप्त करता है अगर हम सिंगलटन डेटा परिभाषाओं में deriving Eq शामिल करते हैं। लाना इस का उपयोग करने के लिए:

    parseSynonym :: forall lt. SLiftedType lt -> String -> String 
    parseSynonym slt flv = withSingI slt $ 
         case (readEither flv :: Either String (At SynonymS lt)) of 
         Left err -> "Can't parse synonym: " ++ err 
         Right (At tag x) -> "Synonym value: " ++ show (Some tag x :: Some SynonymS) 
    

    हम भी At और Some थोड़ा आसान के बीच रूपांतरण कर सकते हैं:

    curry' :: (forall x. At f x -> a) -> Some f -> a 
    curry' f (Some tag x) = f (At tag x) 
    
    uncurry' :: (Some f -> a) -> At f x -> a 
    uncurry' f (At tag x) = f (Some tag x) 
    
    parseSynonym :: forall lt. SLiftedType lt -> String -> String 
    parseSynonym slt flv = withSingI slt $ 
         case (readEither flv :: Either String (At SynonymS lt)) of 
         Left err -> "Can't parse synonym: " ++ err 
         Right atx -> "Synonym value: " ++ uncurry' show atx 
    
  • +0

    मैंने जवाब में 'सिंगलेट्स' के साथ घनिष्ठ परिचितता ग्रहण की। कृपया पूछें कि कुछ अस्पष्ट है या नहीं। –

    0

    अगर मैं सही ढंग से समझ कि आप क्या करना चाहते हैं, यह संभव नहीं है। यदि यह थे, तो आप आसानी से टाइप Proxy t -> Bool की एक गैर निरंतर समारोह,

    data YesNo = Yes | No 
    class Foo (yn :: YesNo) where foo :: Proxy yn -> Bool 
    type family (Foo (T t) => T t) where 
        T X = Yes 
        T y = No 
    
    f :: forall t. Proxy t -> Bool 
    f _ = foo (Proxy (T t)) 
    

    की तर्ज पर निर्माण कर सकता है लेकिन आप इस तरह के एक समारोह, तब भी जब सभी प्रकार शामिल बंद हो जाती हैं (या तो निश्चित रूप से है जो निर्माण नहीं कर सकते एक दृष्टिकोण या जीएचसी की सीमा, आपके दृष्टिकोण के आधार पर)।

    संबंधित मुद्दे