डेटा में बेतुका कार्य क्या है। इसके लिए उपयोगी है?


97

absurdमें समारोह Data.Voidनिम्नलिखित हस्ताक्षर है, जहां है Voidतार्किक निर्जन प्रकार है कि पैकेज द्वारा निर्यात है:

-- | Since 'Void' values logically don't exist, this witnesses the logical
-- reasoning tool of \"ex falso quodlibet\".
absurd :: Void -> a

मुझे दस्तावेज़ की टिप्पणी को प्राप्त करने के लिए पर्याप्त तर्क पता है कि यह प्रस्ताव, जैसे-प्रकार के पत्राचार द्वारा, वैध सूत्र के अनुरूप है ⊥ → a

क्या मैं हैरान और उत्सुक हूं: यह किस प्रकार की व्यावहारिक प्रोग्रामिंग समस्याओं में उपयोगी है? मैं सोच रहा हूं कि शायद यह कुछ मामलों में उपयोगी है, लेकिन "केस नहीं हो सकता" मामलों के एक प्रकार-सुरक्षित तरीके के रूप में, लेकिन मुझे यह जानने के लिए करी-हावर्ड के व्यावहारिक उपयोग के बारे में पर्याप्त नहीं पता है कि क्या विचार है बिल्कुल सही ट्रैक।

संपादित करें: उदाहरण हास्केल में अधिमानतः, लेकिन अगर कोई भी निर्भरतापूर्वक टाइप की गई भाषा का उपयोग करना चाहता है, तो मुझे शिकायत नहीं होगी ...


5
एक त्वरित खोज से पता चलता है कि absurdइस लेख का उपयोग इस लेख में किया गया है जो कि Contमोनाद के साथ काम कर रहा है : haskellforall.com/2012/12/the-continuation-monad.html
Artyom

6
आप देख सकते हैं absurdके बीच समाकृतिकता में से एक दिशा के रूप में Voidऔर forall a. a
डेनियल वैगनर

जवाबों:


61

जीवन थोड़ा कठिन है, क्योंकि हास्केल गैर सख्त है। सामान्य उपयोग मामला असंभव रास्तों को संभालने के लिए है। उदाहरण के लिए

simple :: Either Void a -> a
simple (Left x) = absurd x
simple (Right y) = y

यह कुछ हद तक उपयोगी है। के लिए एक साधारण प्रकार पर विचार करेंPipes

data Pipe a b r
  = Pure r
  | Await (a -> Pipe a b r)
  | Yield !b (Pipe a b r)

यह गैब्रियल गोंजालेस की Pipesलाइब्रेरी से मानक पाइप प्रकार का एक सख्त-ified और सरलीकृत संस्करण है । अब, हम एक ऐसे पाइप को एनकोड कर सकते हैं जो कभी भी पैदावार नहीं करता है (यानी, उपभोक्ता)

type Consumer a r = Pipe a Void r

यह वास्तव में कभी उपज नहीं देता है। इस का निहितार्थ यह है कि एक के लिए उचित गुना नियम है Consumerहै

foldConsumer :: (r -> s) -> ((a -> s) -> s) -> Consumer a r -> s
foldConsumer onPure onAwait p 
 = case p of
     Pure x -> onPure x
     Await f -> onAwait $ \x -> foldConsumer onPure onAwait (f x)
     Yield x _ -> absurd x

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

संभवतः इसका सबसे क्लासिक उपयोग Voidसीपीएस में है।

type Continuation a = a -> Void

यह एक Continuationऐसा कार्य है जो कभी नहीं लौटता है। Continuation"नहीं" का प्रकार संस्करण है। इससे हमें CPS का एक सनद मिलता है (शास्त्रीय तर्क के अनुसार)

newtype CPS a = Continuation (Continuation a)

चूंकि हास्केल शुद्ध है, इसलिए हम इस प्रकार से कुछ भी प्राप्त नहीं कर सकते हैं।


1
हुह, मैं वास्तव में थोड़े उस सीपीएस बिट का अनुसरण कर सकता हूं। मैं निश्चित रूप से करी-हावर्ड डबल नकार / सीपीएस पत्राचार के बारे में पहले सुना था, लेकिन इसे नहीं समझा; मैं दावा नहीं करने जा रहा हूँ मैं इसे पूरी तरह से अब प्राप्त करता हूं, लेकिन यह निश्चित रूप से मदद करता है!
लुइस कैसिलस

"जीवन थोड़ा कठिन है, क्योंकि हास्केल गैर-सख्त है " - आपका वास्तव में क्या मतलब है?
एरिक कपलुन

4
@ ErikAllik, एक सख्त भाषा में, Voidनिर्जन है। हास्केल में, यह शामिल है _|_। एक सख्त भाषा में, एक डेटा कंस्ट्रक्टर जो तर्क का तर्क लेता Voidहै, उसे कभी भी लागू नहीं किया जा सकता है, इसलिए पैटर्न मैच का दाहिना हाथ अप्राप्य है। हास्केल में, आपको !इसे लागू करने के लिए एक का उपयोग करने की आवश्यकता है, और जीएचसी शायद यह ध्यान नहीं देगा कि पथ अनुपलब्ध है।
dfeuer

अगाडा के बारे में कैसे? यह आलसी है, लेकिन क्या यह है _|_? और क्या यह उसी सीमा से पीड़ित है?
एरिक कप्लून

एजडा, आम तौर पर बोल रहा है, कुल और इसलिए मूल्यांकन आदेश अवलोकनीय नहीं है। जब तक आप समाप्ति चेकर या ऐसा कुछ बंद नहीं करते हैं, तब तक खाली प्रकार का कोई बंद शब्द नहीं है
फिलिप जेएफ

58

लैंबडा शब्दों के लिए इस प्रतिनिधित्व पर विचार करें जो उनके मुक्त चर द्वारा पैराट्राइक किया गया है। (बेलीगार्ड और हुक 1994, बर्ड एंड पैटरसन 1999, अल्टेंकिर्च और रीस 1999 द्वारा पत्र देखें।)

data Tm a  = Var a
           | Tm a :$ Tm a
           | Lam (Tm (Maybe a))

आप निश्चित रूप से इसे बना सकते हैं Functor, नाम बदलने की धारणा को कैप्चर कर सकते हैं , और Monadप्रतिस्थापन की धारणा को कैप्चर कर सकते हैं।

instance Functor Tm where
  fmap rho (Var a)   = Var (rho a)
  fmap rho (f :$ s)  = fmap rho f :$ fmap rho s
  fmap rho (Lam t)   = Lam (fmap (fmap rho) t)

instance Monad Tm where
  return = Var
  Var a     >>= sig  = sig a
  (f :$ s)  >>= sig  = (f >>= sig) :$ (s >>= sig)
  Lam t     >>= sig  = Lam (t >>= maybe (Var Nothing) (fmap Just . sig))

अब बंद शब्दों पर विचार करें : ये वहां के निवासी हैं Tm Void। आपको मनमानी मुक्त चर के साथ बंद शर्तों को एम्बेड करने में सक्षम होना चाहिए। कैसे?

fmap absurd :: Tm Void -> Tm a

बेशक, यह है कि यह फ़ंक्शन शब्द को कुछ भी नहीं करने के लिए पीछे ले जाएगा। लेकिन यह एक स्पर्श से अधिक ईमानदार है unsafeCoerce। और इसीलिए vacuousइसे जोड़ा गया Data.Void...

या एक मूल्यांकनकर्ता लिखें। यहाँ मुफ्त चर के साथ मूल्य हैं b

data Val b
  =  b :$$ [Val b]                              -- a stuck application
  |  forall a. LV (a -> Val b) (Tm (Maybe a))   -- we have an incomplete environment

मैंने अभी-अभी बंदियों के रूप में लैंबडास का प्रतिनिधित्व किया है। मूल्यांकनकर्ता एक पर्यावरण है जो aमूल्यों पर मुफ्त चर का मानचित्रण करता है b

eval :: (a -> Val b) -> Tm a -> Val b
eval g (Var a)   = g a
eval g (f :$ s)  = eval g f $$ eval g s where
  (b :$$ vs)  $$ v  = b :$$ (vs ++ [v])         -- stuck application gets longer
  LV g t      $$ v  = eval (maybe v g) t        -- an applied lambda gets unstuck
eval g (Lam t)   = LV g t

आपने यह अनुमान लगाया। किसी भी लक्ष्य पर एक बंद शब्द का मूल्यांकन करने के लिए

eval absurd :: Tm Void -> Val b

अधिक आम तौर पर, Voidशायद ही कभी इसका उपयोग किया जाता है, लेकिन जब आप एक प्रकार के पैरामीटर को एक तरह से तुरंत रोकना चाहते हैं तो यह आसान होता है जो किसी प्रकार की असंभवता (जैसे, यहां, एक बंद अवधि में मुफ्त चर का उपयोग करके) को इंगित करता है। अक्सर इन parametrized प्रकार उच्च क्रम कार्यों के संचालन के लिए पूरे प्रकार पर मानकों के आधार पर संचालन उठाने के साथ आते हैं (जैसे, यहाँ, fmap, >>=, eval)। तो आप absurdसामान्य-उद्देश्य ऑपरेशन के रूप में पास करते हैं Void

एक अन्य उदाहरण के लिए, Either e vकम्प्यूटेशन पर कब्जा करने के लिए उपयोग करने की कल्पना करें, जो आपको उम्मीद है कि आप एक vप्रकार का अपवाद उठा सकते हैं e। आप समान रूप से बुरे व्यवहार के जोखिम के दस्तावेज़ के लिए इस दृष्टिकोण का उपयोग कर सकते हैं। इस सेटिंग में पूरी तरह से अच्छी तरह से व्यवहार किए गए संगणना के eलिए Void, फिर से उपयोग करें

either absurd id :: Either Void v -> v

सुरक्षित रूप से चलाने के लिए या

either absurd Right :: Either Void v -> Either e v

एक असुरक्षित दुनिया में सुरक्षित घटकों को एम्बेड करने के लिए।

ओह, और एक आखिरी तूफान, "नहीं हो सकता" को संभालना। यह सामान्य जिपर निर्माण में दिखाता है, हर जगह कि कर्सर नहीं हो सकता है।

class Differentiable f where
  type D f :: * -> *              -- an f with a hole
  plug :: (D f x, x) -> f x       -- plugging a child in the hole

newtype K a     x  = K a          -- no children, just a label
newtype I       x  = I x          -- one child
data (f :+: g)  x  = L (f x)      -- choice
                   | R (g x)
data (f :*: g)  x  = f x :&: g x  -- pairing

instance Differentiable (K a) where
  type D (K a) = K Void           -- no children, so no way to make a hole
  plug (K v, x) = absurd v        -- can't reinvent the label, so deny the hole!

मैंने बाकी को हटाने का फैसला किया, भले ही यह बिल्कुल प्रासंगिक न हो।

instance Differentiable I where
  type D I = K ()
  plug (K (), x) = I x

instance (Differentiable f, Differentiable g) => Differentiable (f :+: g) where
  type D (f :+: g) = D f :+: D g
  plug (L df, x) = L (plug (df, x))
  plug (R dg, x) = R (plug (dg, x))

instance (Differentiable f, Differentiable g) => Differentiable (f :*: g) where
  type D (f :*: g) = (D f :*: g) :+: (f :*: D g)
  plug (L (df :&: g), x) = plug (df, x) :&: g
  plug (R (f :&: dg), x) = f :&: plug (dg, x)

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

data Term f x = Var x | Con (f (Term f x))   -- the Free monad, yet again

किसी भी वाक्य रचना में एक Differentiableऔर Traversableफ़नकार से आज़ादी से उत्पन्न f। हम का उपयोग Term f Voidनहीं मुक्त चर के साथ क्षेत्रों का प्रतिनिधित्व करने के लिए, और [D f (Term f Void)]प्रतिनिधित्व करने के लिए ट्यूबों कोई मुफ्त चर या तो एक पृथक मुक्त चर करने के लिए, या दो या अधिक मुक्त चर के रास्तों में एक जंक्शन के साथ क्षेत्रों के माध्यम से सुरंग। उस लेख को कुछ समय समाप्त करना चाहिए।

एक प्रकार के लिए जिसमें कोई मान नहीं है (या कम से कम, विनम्र कंपनी में बोलने लायक नहीं है), Voidउल्लेखनीय रूप से उपयोगी है। और absurdआप इसका उपयोग कैसे करते हैं।


चाहेंगे forall f. vacuous f = unsafeCoerce fएक वैध GHC पुनर्लेखन नियम हो सकता है?
कैक्टस

1
@ कैक्टस, वास्तव में नहीं। फर्जी Functorउदाहरण GADT हो सकते हैं जो वास्तव में कुछ भी नहीं हैं जैसे कि फंक्शंस।
dfeuer

क्या वे नियम Functorनहीं तोड़ेंगे fmap id = id? या यह है कि आप यहाँ "फर्जी" से क्या मतलब है?
कैक्टस

35

मैं सोच रहा हूं कि शायद कुछ मामलों में यह उपयोगी है क्योंकि एग्जॉस्ट से निपटने का एक सुरक्षित तरीका "केस नहीं हो सकता है"

यह ठीक है।

आप कह सकते हैं कि absurdइससे ज्यादा उपयोगी कोई और नहीं है const (error "Impossible")। हालांकि, यह प्रकार प्रतिबंधित है, ताकि इसका एकमात्र इनपुट कुछ प्रकार का हो सकता है Void, एक डेटा प्रकार जो जानबूझकर निर्जन छोड़ दिया जाता है। इसका मतलब है कि कोई वास्तविक मूल्य नहीं है जिसे आप पास कर सकते हैं absurd। यदि आप कभी भी कोड की एक शाखा में समाप्त होते हैं जहां टाइप चेकर को लगता है कि आपके पास कुछ प्रकार की पहुंच है Void, तो, ठीक है, आप एक बेतुकी स्थिति में हैं। तो आप बस absurdमूल रूप से चिह्नित करने के लिए उपयोग करते हैं कि कोड की इस शाखा तक कभी नहीं पहुंचा जाना चाहिए।

"एक्स फाल्स क्वॉडलिबेट" का शाब्दिक अर्थ है "से [अ] असत्य [प्रस्ताव], कुछ भी"। इसलिए जब आप पाते हैं कि आप डेटा का एक टुकड़ा धारण कर रहे हैं जिसका प्रकार है Void, तो आप जानते हैं कि आपके हाथों में झूठे सबूत हैं। आप इसलिए (इच्छित ) किसी भी छेद को भर सकते हैं absurd, क्योंकि एक झूठे प्रस्ताव से, कुछ भी अनुसरण करता है।

मैंने कोंडिट के पीछे के विचारों के बारे में एक ब्लॉग पोस्ट लिखी है जिसमें उपयोग करने का एक उदाहरण है absurd

http://unknownparallel.wordpress.com/2012/07/30/pipes-to-conduits-part-6-leftovers/#running-a-pipeline


13

आम तौर पर, आप इसका उपयोग जाहिरा तौर पर आंशिक पैटर्न के मैचों से बचने के लिए कर सकते हैं। उदाहरण के लिए, इस उत्तर से डेटा प्रकार की घोषणाओं का एक अनुमान हथियाना :

data RuleSet a            = Known !a | Unknown String
data GoRuleChoices        = Japanese | Chinese
type LinesOfActionChoices = Void
type GoRuleSet            = RuleSet GoRuleChoices
type LinesOfActionRuleSet = RuleSet LinesOfActionChoices

फिर आप absurdइस तरह का उपयोग कर सकते हैं , उदाहरण के लिए:

handleLOARules :: (String -> a) -> LinesOfActionsRuleSet -> a
handleLOARules f r = case r of
    Known   a -> absurd a
    Unknown s -> f s

13

खाली डेटा प्रकार का प्रतिनिधित्व करने के विभिन्न तरीके हैं । एक एक खाली बीजीय डेटा प्रकार है। दूसरा तरीका यह है कि इसे isα.α या के लिए एक अन्य नाम दें

type Void' = forall a . a

हास्केल में - यह है कि हम इसे सिस्टम एफ में कैसे एन्कोड कर सकते हैं ( प्रूफ और प्रकार के अध्याय 11 देखें )। ये दो वर्णन बेशक आइसोमॉर्फिक हैं और आइसोमॉर्फ़िज्म द्वारा \x -> x :: (forall a.a) -> Voidऔर इसके द्वारा देखा जाता है absurd :: Void -> a

कुछ मामलों में, हम स्पष्ट प्रकार को पसंद करते हैं, आमतौर पर यदि खाली डेटा प्रकार किसी फ़ंक्शन के तर्क में, या अधिक जटिल डेटा प्रकार में प्रकट होता है, जैसे कि Data.Conduit :

type Sink i m r = Pipe i i Void () m r

कुछ मामलों में, हम बहुरूपी संस्करण को पसंद करते हैं, आमतौर पर खाली डेटा प्रकार एक फ़ंक्शन के रिटर्न प्रकार में शामिल होता है।

absurd उठता है जब हम इन दो अभ्यावेदन के बीच परिवर्तित कर रहे हैं।


उदाहरण के लिए, callcc :: ((a -> m b) -> m a) -> m aउपयोग (निहित) forall b। यह प्रकार का भी हो सकता है ((a -> m Void) -> m a) -> m a, क्योंकि प्रतियोगिता के लिए कॉल वास्तव में वापस नहीं आती है, यह नियंत्रण को दूसरे बिंदु पर स्थानांतरित करता है। यदि हम निरंतरता के साथ काम करना चाहते हैं, तो हम परिभाषित कर सकते हैं

type Continuation r a = a -> Cont r Void

(हम उपयोग कर सकते हैं, type Continuation' r a = forall b . a -> Cont r bलेकिन इसके लिए रैंक 2 प्रकारों की आवश्यकता होगी।) और फिर, इसे vacuousMधर्मान्तरित करता Cont r Voidहै Cont r b

(यह भी ध्यान दें कि आप haskellers.com का उपयोग एक निश्चित पैकेज के उपयोग (रिवर्स निर्भरता) की खोज के लिए कर सकते हैं , जैसे कि कौन और कैसे शून्य पैकेज का उपयोग करता है, यह देखने के लिए ।)


TypeApplicationsके विवरण के बारे में अधिक स्पष्ट होने के लिए इस्तेमाल किया जा सकता proof :: (forall a. a) -> Void: proof fls = fls @Void
आइसलैंड_जैक

1

इदरीस जैसी भरोसेमंद टाइप की भाषाओं में, यह शायद हास्केल की तुलना में अधिक उपयोगी है। आमतौर पर, कुल फ़ंक्शन में, जब आप पैटर्न एक ऐसे मूल्य से मेल खाते हैं जो वास्तव में फ़ंक्शन में छायांकित नहीं किया जा सकता है, तो आप निर्जन प्रकार के मान का निर्माण करेंगे और absurdकेस की परिभाषा को अंतिम रूप देने के लिए उपयोग करेंगे।

उदाहरण के लिए यह फ़ंक्शन किसी तत्व को सूची से उस प्रकार-स्तरीय कॉस्ट्रेन्ट के साथ हटाता है जो वहां मौजूद है:

shrink : (xs : Vect (S n) a) -> Elem x xs -> Vect n a
shrink (x :: ys) Here = ys
shrink (y :: []) (There p) = absurd p
shrink (y :: (x :: xs)) (There p) = y :: shrink (x :: xs) p

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

करी-हावर्ड बिंदु के माध्यम से, जहां प्रस्ताव हैं, फिर absurdविरोधाभास द्वारा एक सबूत में QED की तरह है।

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