2012-08-24 14 views
7

कभी-कभी मुझे अस्तित्व में प्रमाणित प्रकार के मान वापस करने की आवश्यकता होती है। ऐसा अक्सर होता है जब मैं प्रेत प्रकारों के साथ काम कर रहा हूं (उदाहरण के लिए एक संतुलित पेड़ की गहराई का प्रतिनिधित्व करना)। AFAIK जीएचसी में exists क्वांटिफायर नहीं है। यह केवल existentially quantified data types (या तो सीधे या जीएडीटी का उपयोग कर) की अनुमति देता है।फ़ंक्शन रिटर्न प्रकारों में मौजूद मात्रात्मक मात्रा को सिमुलेट करना

एक उदाहरण देने के लिए, मैं इस तरह कार्य करना चाहते हैं: अब तक

, मैं 2 संभव समाधान मैं एक जवाब के रूप में शामिल कर देंगे कि है, मुझे पता है कि खुशी होगी अगर कोई बेहतर या अलग कुछ जानता है।

उत्तर

10

मानक समाधान एक अस्तित्व में मात्रात्मक डेटा प्रकार बनाना है। परिणाम

{-# LANGUAGE ExistentialQuantification #-} 

data Exists1 = forall a . (Show a) => Exists1 a 
instance Show Exists1 where 
    showsPrec _ (Exists1 x) = shows x 

somethingPrintable1 :: Int -> Exists1 
somethingPrintable1 x = Exists1 x 

की तरह कुछ अब हो सकता है, एक स्वतंत्र रूप से show (somethingPrintable 42) उपयोग कर सकते हैं। Exists1newtype नहीं हो सकता है, मुझे लगता है कि ऐसा इसलिए है क्योंकि एक छिपे हुए संदर्भ शब्दकोश में show के विशेष कार्यान्वयन को पार करना आवश्यक है।

प्रकार सुरक्षित वैक्टर के लिए, एक आगे बढ़ सके fromList1 कार्यान्वयन के लिए उसी तरह:

{-# LANGUAGE GADTs #-} 

data Zero 
data Succ n 

data Vec a n where 
    Nil ::     Vec a Zero 
    Cons :: a -> Vec a n -> Vec a (Succ n) 

data Exists1 f where 
    Exists1 :: f a -> Exists1 f 

fromList1 :: [a] -> Exists1 (Vec a) 
fromList1 [] = Exists1 Nil 
fromList1 (x:xs) = case fromList1 xs of 
        Exists1 r -> Exists1 $ Cons x r 

यह अच्छी तरह से काम करता है, लेकिन मुख्य दोष यह है मैं देख रहा हूँ अतिरिक्त निर्माता है। fromList1 पर प्रत्येक कॉल के परिणामस्वरूप कन्स्ट्रक्टर के एक आवेदन में परिणाम होता है, जिसे तुरंत डीकोनस्ट्रक्टेड किया जाता है। पहले की तरह, newtypeExists1 के लिए संभव नहीं है, लेकिन मुझे लगता है कि किसी भी प्रकार की कक्षा की बाधाओं के बिना संकलक इसे अनुमति दे सकता है।


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

{-# LANGUAGE Rank2Types #-} 

somethingPrintable2 :: Int -> ((forall a . (Show a) => a -> r) -> r) 
somethingPrintable2 x = \c -> c x 

अब एक somethingPrintable 42 show का उपयोग परिणाम प्राप्त करने के हैं।

और, Vec डेटा प्रकार के लिए:

{-# LANGUAGE RankNTypes, GADTs #-} 

fromList2 :: [a] -> ((forall n . Vec a n -> r) -> r) 
fromList2 [] c  = c Nil 
fromList2 (x:xs) c = fromList2 xs (c . Cons x) 

-- Or wrapped as a newtype 
-- (this is where we need RankN instead of just Rank2): 
newtype Exists3 f r = Exists3 { unexists3 :: ((forall a . f a -> r) -> r) } 

fromList3 :: [a] -> Exists3 (Vec a) r 
fromList3 []  = Exists3 (\c -> c Nil) 
fromList3 (x:xs) = Exists3 (\c -> unexists3 (fromList3 xs) (c . Cons x)) 

इस में कुछ सहायक कार्यों का उपयोग कर में थोड़ा और अधिक पठनीय बनाया जा सकता है:

-- | A helper function for creating existential values. 
exists3 :: f x -> Exists3 f r 
exists3 x = Exists3 (\c -> c x) 
{-# INLINE exists3 #-} 

-- | A helper function to mimic function application. 
(?$) :: (forall a . f a -> r) -> Exists3 f r -> r 
(?$) f x = unexists3 x f 
{-# INLINE (?$) #-} 

fromList3 :: [a] -> Exists3 (Vec a) r 
fromList3 []  = exists3 Nil 
fromList3 (x:xs) = (exists3 . Cons x) ?$ fromList3 xs 

मुख्य नुकसान मैं यहाँ देख रहे हैं:

  1. अतिरिक्त फ़ंक्शन एप्लिकेशन के साथ संभावित ओवरहेड (मुझे नहीं पता कि संकलक कितना चुन सकता है इसे imize)।
  2. कम पठनीय कोड (कम से कम लोगों के लिए निरंतरता के लिए उपयोग नहीं किया जाता है)।
संबंधित मुद्दे