2014-09-15 7 views
14

परिचय और उदाहरण उपयोग के मामलेAmbigous उदाहरण संकल्प

नमस्कार! मुझे हास्केल में एक समस्या है। आइए निम्नलिखित कोड

class PolyMonad m1 m2 m3 | m1 m2 -> m3 where 
    polyBind :: m1 a -> (a -> m2 b) -> m3 b 

जो कि एक पॉली मोनैड बाध्यकारी घोषित करता है। एक अच्छा उदाहरण उपयोग स्थिति होगा:

newtype Pure a = Pure { fromPure :: a } deriving (Show) 

instance PolyMonad Pure Pure Pure where 
    polyBind a f = f (fromPure a) 

instance PolyMonad Pure IO IO where 
    polyBind a f = f (fromPure a) 

instance PolyMonad IO Pure IO where 
    polyBind a f = (fromPure . f) <$> a 

instance PolyMonad IO IO IO where 
    polyBind a f = a >>= f 

और -XRebindableSyntax इसलिए की तरह साथ इसे का उपयोग:

test = do 
    Pure 5 
    print "hello" 
    Pure() 

लेकिन हम भी बहुत कुछ इसके साथ क्या कर सकते हैं - यह केवल एक आपको एक उदाहरण दिखाने के लिए परीक्षण किया गया था मामला।

समस्या

एक छोटे से अधिक जटिल उपयोग पर विचार करने देता है। मैं एक पॉलिमोनैड-जैसी कक्षा लिखना चाहता हूं, जो हमेशा m3 b आउटपुट नहीं करेगा, लेकिन कुछ विशिष्ट मामलों में यह को एक विशिष्ट X के लिए आउटपुट करेगा। सादगी के लिए, मान लीजिए कि हम केवल m3 (X b) आउटपुट करना चाहते हैं, जब m1 या m2IO था।

ऐसा लगता है कि हम इसे लचीलापन खोए बिना हास्केल में अभी नहीं कर सकते हैं। मैं निम्नलिखित कार्य की जरूरत है किसी भी प्रकार की जानकारियां जोड़े बिना संकलित करने के लिए (हास्केल कोड एक उत्पन्न एक है):

tst1 x = x `polyBind` (\_ -> Pure 0) 
tst2 = (Pure 1) `polyBind` (\_ -> Pure 0) 
tst3 x y = x `polyBind` (\_ -> y `polyBind` (\_ -> Pure 0)) 

वैसे भी इन कार्यों में अच्छी तरह से PolyMonad वर्ग का उपयोग कर संकलन।

Fundep समस्या

प्रयासों में से एक हो सकता है हल करने के लिए प्रयास:

instance PolyMonad2 Pure Pure b (Pure b) where 
    polyBind2 a f = f (fromPure a) 

instance PolyMonad2 Pure IO b (IO (X b)) where 
    polyBind2 a f = fmap X $ f (fromPure a) 

-- ... 
:

class PolyMonad2 m1 m2 m3 b | m1 m2 b -> out where 
    polyBind2 :: m1 a -> (a -> m2 b) -> out 

और निश्चित रूप से हम आसानी से सभी आवश्यक उदाहरणों, की तरह लिख सकते हैं

लेकिन के बजाय polyBind2 का उपयोग करते समय हमारे परीक्षण फ़ंक्शंस संकलित नहीं होंगे।

Could not deduce (PolyMonad2 m1 Pure b0 out) 
    arising from the ambiguity check for ‘tst1’ 
from the context (PolyMonad2 m1 Pure b out, Num b) 
    bound by the inferred type for ‘tst1’: 
      (PolyMonad2 m1 Pure b out, Num b) => m1 a -> out 
    at /tmp/Problem.hs:51:1-37 
The type variable ‘b0’ is ambiguous 
When checking that ‘tst1’ 
    has the inferred type ‘forall (m1 :: * -> *) b out a. 
         (PolyMonad2 m1 Pure b out, Num b) => 
         m1 a -> out’ 
Probable cause: the inferred type is ambiguous 

बंद प्रकार परिवारों, समस्या

एक थोड़ा बेहतर तरीका का समाधान करने के लिए यहाँ closed type families उपयोग करने के लिए किया जाएगा प्रयास की तरह:

पहले समारोह ( tst1 x = x polyBind2 (\_ -> Pure 0)) एक संकलन त्रुटि आउटपुट
class PolyMonad3 m1 m2 where 
    polyBind3 :: m1 a -> (a -> m2 b) -> OutputOf m1 m2 b 

type family OutputOf m1 m2 a where 
    OutputOf Pure Pure a = Pure a 
    OutputOf x y a = Pure (X a) 

लेकिन फिर tst1 फ़ंक्शन संकलित करने का प्रयास करते समय (tst1 x = x पॉलीबिंड 3 (\_ -> Pure 0)) हम एक और संकलन समय त्रुटि मिलती है:

Could not deduce (OutputOf m1 Pure b0 ~ OutputOf m1 Pure b) 
from the context (PolyMonad3 m1 Pure, Num b) 
    bound by the inferred type for ‘tst1’: 
      (PolyMonad3 m1 Pure, Num b) => m1 a -> OutputOf m1 Pure b 
    at /tmp/Problem.hs:59:1-37 
NB: ‘OutputOf’ is a type function, and may not be injective 
The type variable ‘b0’ is ambiguous 
Expected type: m1 a -> OutputOf m1 Pure b 
    Actual type: m1 a -> OutputOf m1 Pure b0 
When checking that ‘tst1’ 
    has the inferred type ‘forall (m1 :: * -> *) a b. 
         (PolyMonad3 m1 Pure, Num b) => 
         m1 a -> OutputOf m1 Pure b’ 
Probable cause: the inferred type is ambiguous 

एक hacky प्रयास यह करने के लिए चारों ओर

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

class PolyMonad4 m1 m2 b out | m1 m2 b -> out, out -> b where 
    polyBind4 :: m1 a -> (a -> m2 b) -> out 
बेशक

कार्यात्मक निर्भरता out -> b, बस गलत है, क्योंकि हम जैसे ऐसे मामलों को परिभाषित नहीं कर सकते हैं:: चलो निम्नलिखित प्रकार वर्ग पर विचार

instance PolyMonad4 Pure IO b (IO (X b)) where 
    polyBind4 a f = fmap X $ f (fromPure a) 

instance PolyMonad4 IO IO b (IO b) where 
    polyBind4 = undefined 

लेकिन यह साथ खेलने की सुविधा देता है और उन्हें इतना घोषित (-XUndecidableInstances का उपयोग कर):

instance out~(Pure b) => PolyMonad4 Pure Pure b out where 
    polyBind4 a f = f (fromPure a) 

instance out~(IO(X b)) => PolyMonad4 Pure IO b out where 
    polyBind4 a f = fmap X $ f (fromPure a) 

instance out~(IO b) => PolyMonad4 IO IO b out where 
    polyBind4 = undefined 

instance out~(IO(X b)) => PolyMonad4 IO Pure b out where 
    polyBind4 = undefined 

क्या मज़ेदार है, हमारे परीक्षण कार्यों में से कुछ संकलन और काम, अर्थात् करता है:

+०१२३५१६४१०६
tst1' x = x `polyBind4` (\_ -> Pure 0) 
tst2' = (Pure 1) `polyBind4` (\_ -> Pure 0) 

लेकिन यह नहीं एक:

tst3' x y = x `polyBind4` (\_ -> y `polyBind4` (\_ -> Pure 0)) 

संकलन समय त्रुटि में परिणाम:

Could not deduce (PolyMonad4 m3 Pure b0 (m20 b)) 
    arising from the ambiguity check for ‘tst3'’ 
from the context (PolyMonad4 m3 Pure b1 (m2 b), 
        PolyMonad4 m1 m2 b out, 
        Num b1) 
    bound by the inferred type for ‘tst3'’: 
      (PolyMonad4 m3 Pure b1 (m2 b), PolyMonad4 m1 m2 b out, Num b1) => 
      m1 a -> m3 a1 -> out 
    at /tmp/Problem.hs:104:1-62 
The type variables ‘m20’, ‘b0’ are ambiguous 
When checking that ‘tst3'’ 
    has the inferred type ‘forall (m1 :: * -> *) 
           (m2 :: * -> *) 
           b 
           out 
           a 
           (m3 :: * -> *) 
           b1 
           a1. 
         (PolyMonad4 m3 Pure b1 (m2 b), PolyMonad4 m1 m2 b out, Num b1) => 
         m1 a -> m3 a1 -> out’ 
Probable cause: the inferred type is ambiguous 

इससे भी अधिक hacky प्रयास newtype का उपयोग कर

लपेटकर मैं इसे और भी अधिक है बताया हैकी, क्योंकि यह हमें -XIncoherentInstances का उपयोग करने की ओर ले जाता है, जोहैं। एक विचार यह है newtype आवरण लिखने के लिए निश्चित रूप से हो सकता है:

newtype XWrapper m a = XWrapper (m (X (a))) 

और कुछ utils खोल दे रहे हैं:

instance PolyMonad Pure Pure Pure 
instance PolyMonad Pure IO (XWrapper IO) 
instance PolyMonad IO Pure (XWrapper IO) 
instance PolyMonad IO IO IO 

लेकिन:

class UnpackWrapper a b | a -> b where 
    unpackWrapper :: a -> b 

instance UnpackWrapper (XWrapper m a) (m (X a)) where 
    unpackWrapper (XWrapper a) = a 

instance UnpackWrapper (Pure a) (Pure a) where 
    unpackWrapper = id 

instance UnpackWrapper (IO a) (IO a) where 
    unpackWrapper = id 

अब हम easly निम्नलिखित उदाहरणों घोषणा कर सकते हैं फिर, हम बाध्य और अनचाहे कार्यों को संयोजित करते समय हमारे परीक्षण नहीं चला सकते:

polyBindUnwrap a f = unpackWrapper $ polyBind a f 

परीक्षण फ़ंक्शन फिर से संकलित करने में विफल रहते हैं। हम यहां कुछ -XIncoherentInstances (अंत में कोड सूची देखें) के साथ गड़बड़ कर सकते हैं, लेकिन मुझे अभी तक कोई अच्छा परिणाम नहीं मिला है।

अंतिम सवाल

यह एक समस्या है जो वर्तमान GHC हास्केल कार्यान्वयन का उपयोग नहीं किया जा सकता है?

{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE FunctionalDependencies #-} 
{-# LANGUAGE TypeFamilies #-} 
{-# LANGUAGE UndecidableInstances #-} 

import Control.Applicative 

class PolyMonad m1 m2 m3 | m1 m2 -> m3 where 
    polyBind :: m1 a -> (a -> m2 b) -> m3 b 


---------------------------------------------------------------------- 
-- Some utils 
---------------------------------------------------------------------- 

newtype Pure a = Pure { fromPure :: a } deriving (Show) 
newtype X a = X { fromX :: a } deriving (Show) 

main = return() 

---------------------------------------------------------------------- 
-- Example use cases 
---------------------------------------------------------------------- 

instance PolyMonad Pure Pure Pure where 
    polyBind a f = f (fromPure a) 

instance PolyMonad Pure IO IO where 
    polyBind a f = f (fromPure a) 

instance PolyMonad IO Pure IO where 
    polyBind a f = (fromPure . f) <$> a 

instance PolyMonad IO IO IO where 
    polyBind a f = a >>= f 

-- works when using rebindable syntax 
--test = do 
-- Pure 5 
-- print "hello" 
-- Pure() 

tst1 x = x `polyBind` (\_ -> Pure 0) 
tst2 = (Pure 1) `polyBind` (\_ -> Pure 0) 
tst3 x y = x `polyBind` (\_ -> y `polyBind` (\_ -> Pure 0)) 

---------------------------------------------------------------------- 
-- First attempt to solve the problem 
---------------------------------------------------------------------- 


class PolyMonad2 m1 m2 b out | m1 m2 b -> out where 
    polyBind2 :: m1 a -> (a -> m2 b) -> out 


instance PolyMonad2 Pure Pure b (Pure b) where 
    polyBind2 a f = f (fromPure a) 

instance PolyMonad2 Pure IO b (IO (X b)) where 
    polyBind2 a f = fmap X $ f (fromPure a) 

-- ... 

-- tst1 x = x `polyBind2` (\_ -> Pure 0) -- does NOT compile 


---------------------------------------------------------------------- 
-- Second attempt to solve the problem 
---------------------------------------------------------------------- 

class PolyMonad3 m1 m2 where 
    polyBind3 :: m1 a -> (a -> m2 b) -> OutputOf m1 m2 b 

type family OutputOf m1 m2 a where 
    OutputOf Pure Pure a = Pure a 
    OutputOf x y a = Pure (X a) 

-- tst1 x = x `polyBind3` (\_ -> Pure 0) -- does NOT compile 


---------------------------------------------------------------------- 
-- Third attempt to solve the problem 
---------------------------------------------------------------------- 

class PolyMonad4 m1 m2 b out | m1 m2 b -> out, out -> b where 
    polyBind4 :: m1 a -> (a -> m2 b) -> out 


instance out~(Pure b) => PolyMonad4 Pure Pure b out where 
    polyBind4 a f = f (fromPure a) 

instance out~(IO(X b)) => PolyMonad4 Pure IO b out where 
    polyBind4 a f = fmap X $ f (fromPure a) 

instance out~(IO b) => PolyMonad4 IO IO b out where 
    polyBind4 = undefined 

instance out~(IO(X b)) => PolyMonad4 IO Pure b out where 
    polyBind4 = undefined 


tst1' x = x `polyBind4` (\_ -> Pure 0) 
tst2' = (Pure 1) `polyBind4` (\_ -> Pure 0) 
--tst3' x y = x `polyBind4` (\_ -> y `polyBind4` (\_ -> Pure 0)) -- does NOT compile 


---------------------------------------------------------------------- 
-- Fourth attempt to solve the problem 
---------------------------------------------------------------------- 

class PolyMonad6 m1 m2 m3 | m1 m2 -> m3 where 
    polyBind6 :: m1 a -> (a -> m2 b) -> m3 b 

newtype XWrapper m a = XWrapper (m (X (a))) 


class UnpackWrapper a b | a -> b where 
    unpackWrapper :: a -> b 

instance UnpackWrapper (XWrapper m a) (m (X a)) where 
    unpackWrapper (XWrapper a) = a 

instance UnpackWrapper (Pure a) (Pure a) where 
    unpackWrapper = id 

instance UnpackWrapper (IO a) (IO a) where 
    unpackWrapper = id 

--instance (a1~a2, out~(m a2)) => UnpackWrapper (m a1) out where 
-- unpackWrapper = id 


--{-# LANGUAGE OverlappingInstances #-} 
--{-# LANGUAGE IncoherentInstances #-} 

instance PolyMonad6 Pure Pure Pure where 
    polyBind6 = undefined 

instance PolyMonad6 Pure IO (XWrapper IO) where 
    polyBind6 = undefined 

instance PolyMonad6 IO Pure (XWrapper IO) where 
    polyBind6 = undefined 

instance PolyMonad6 IO IO IO where 
    polyBind6 = undefined 

--polyBind6' a f = unpackWrapper $ polyBind6 a f 

--tst1'' x = x `polyBind6'` (\_ -> Pure 0) 
--tst2'' = (Pure 1) `polyBind4` (\_ -> Pure 0) 
--tst3'' x y = x `polyBind4` (\_ -> y `polyBind4` (\_ -> Pure 0)) -- does NOT compile 

उत्तर

3

मैं इस सवाल injective प्रकार परिवारों पर टिका नहीं लगता कि:

पूर्ण कोड लिस्टिंग

यहाँ एक पूर्ण कोड सूची है, जो GHC> = 7.8 में चलाया जा सकता है है।

injective प्रकार परिवारों बिट बंद प्रकार परिवार OutputOf चारों ओर त्रुटि संदेश में बताया गया है। लेकिन, यह फ़ंक्शन वास्तव में इंजेक्शन नहीं है - इसके लिए दूसरा समीकरण x और y के लिए अनुमति देता है। जीएचसी उपयोगकर्ताओं को इस तथ्य के बारे में याद दिलाना पसंद करता है कि यह परिवारों के प्रकार के लिए इंजेक्शनिटी विश्लेषण नहीं करता है, लेकिन कभी-कभी (यहां की तरह), यह चेतावनी सहायक नहीं है।

इसके बजाय, समस्याओं को आप सब का सामना कर रहे अतिभारित संख्या से उत्पन्न करने लगते हैं। जब आप Pure 0 कहते हैं, तो जीएचसी सही प्रकार Num a => Pure a टाइप करता है। समस्या यह है कि टाइप-स्तरीय फीचर्स जो आप एक्सेस कर रहे हैं (टाइप क्लास रिज़ॉल्यूशन, कार्यात्मक निर्भरताएं, प्रकार के परिवार) बहुत सावधानी बरतें a के लिए कौन सी विशिष्ट पसंद की जाती है। उदाहरण के लिए, यह संभव है कि Integer के लिए Int के लिए आपके दृष्टिकोण में से कोई भी अलग-अलग व्यवहार करता है। (उदाहरण के लिए, आप OutputOf में PolyMonad2 के उदाहरण या अतिरिक्त समीकरण भिन्न हो सकता था।)

इस सब का हल RebindableSyntax का उपयोग करें और परिभाषित fromInteger monomorphic होने के लिए, इस प्रकार सांख्यिक प्रकार फिक्सिंग और परेशानी से बचने के हो सकता है।

+0

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

2

मेरा मानना ​​है कि मौलिक अंतर है कि यहाँ है:

class PolyMonad m1 m2 m3 | m1 m2 -> m3 where 
    polyBind :: m1 a -> (a -> m2 b) -> m3 b 

b पूरी तरह से बहुरूपी है; यह प्रकार वर्ग के लिए एक पैरामीटर नहीं है, तो उदाहरण चुना जा सकता है और कार्यात्मक निर्भरता b की स्वतंत्र रूप से m1 से m3 और m2 निर्धारित करने के लिए आवेदन किया। यह दो स्थानों पर भी दिखाई देता है; प्रकार inferencer परिणाम प्रकार जानता है या समारोह polyBind के लिए पारित के प्रकार, तो यह पर्याप्त रूप से b निर्धारित कर सकते हैं। और Num b => b की तरह एक प्रकार खुशी से polyBind के कई अनुप्रयोगों "के माध्यम से प्रवाह" जाएगा जब तक यह एक जगह है कि एक ठोस प्रकार ठीक करता है में प्रयोग किया जाता है। हालांकि मुझे लगता है कि यह केवल मोनोमोर्फिज्म प्रतिबंध ही उस प्रकार को डिफॉल्ट कर सकता है जो आपको इस मामले में एक अस्पष्ट प्रकार परिवर्तनीय त्रुटि से बचाता है (ठीक उसी तरह जो इसे करने के लिए डिज़ाइन किया गया था)।

यहाँ जबकि:

class PolyMonad2 m1 m2 m3 b | m1 m2 b -> out where 
    polyBind2 :: m1 a -> (a -> m2 b) -> out 

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

और अगर b एक बहुरूपी संख्या Num b => b और out इसके उपयोग से विवश है प्रपत्र Num c => m c (कुछ प्रकार निर्माता m के लिए) का होना है, वहाँ कोई कारण नहीं c और bहीNum होना जरूरी है कि उदाहरण। तो polyBind2 कॉल संख्या पर काम का एक श्रृंखलित श्रृंखला में, हर मध्यवर्ती परिणाम एक अलग Num उदाहरण का उपयोग किया जा सकता है, और उनमें से किसी को जानने के बिना सही PolyMonad2 उदाहरणों कि out में कुछ के साथ b को एकजुट करते चयन करने के लिए कोई तरीका नहीं है। टाइपिंग डिफ़ॉल्ट केवल तभी लागू होती है जब चर पर सभी बाधाएं संख्यात्मक प्रीलूड कक्षाएं हों, लेकिन यहां b बाधा PolyMonad2 m1 m2 m3 b में शामिल है, इसलिए इसे डिफ़ॉल्ट नहीं किया जा सकता है (जो शायद एक अच्छी बात है, क्योंकि आप किस प्रकार का चयन कर सकते हैं किस उदाहरण का उपयोग किया जाता है और प्रोग्राम व्यवहार को नाटकीय रूप से बदलता है; यह केवल संख्यात्मक वर्ग है जो एक दूसरे के "अनुमान" के रूप में जाना जाता है, ताकि यदि प्रोग्राम किस उदाहरण के उपयोग के बारे में अस्पष्ट है तो यह केवल मध्यस्थ रूप से एक को चुनने के लिए अर्ध-उचित है अस्पष्टता के बारे में शिकायत करने के बजाय)।

ही वास्तव में m1, m2, और b से out का निर्धारण जहाँ तक मुझे पता है, चाहे वह कार्यात्मक निर्भरता, प्रकार परिवारों, या कुछ और है के लिए किसी भी विधि के लिए चला जाता है। मुझे यकीन नहीं है कि इस मुद्दे को वास्तव में कैसे हल किया जाए, हालांकि, अधिक प्रकार की टिप्पणियां प्रदान किए बिना।

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