2012-08-29 24 views
8

का उपयोग कर पैरामीटर के साथ एक मोनाडिक वैरैडिक फ़ंक्शन कैसे लिखना है, मैं एक मोनाडिक रिटर्न प्रकार के साथ एक वैरैडिक फ़ंक्शन बनाने की कोशिश कर रहा हूं, जिसके पैरामीटर को भी monadic संदर्भ की आवश्यकता होती है। एकहास्केल: मोनैडिक संदर्भ

असल में, मुझे मिल गया है: (जैसे printfIO() लौट सकते हैं, लेकिन यह अलग है कि इसके मापदंडों एक ही इलाज कर रहे हैं कि क्या यह IO() या String किया जा रहा समाप्त होता है है मुझे यकीन है कि कैसे है कि दूसरी बात का वर्णन करने के लिए नहीं कर रहा हूँ।) डेटा कन्स्ट्रक्टर जो कहता है, दो Char पैरामीटर। मैं इसके बजाय दो सूचक शैली ID Char तर्क प्रदान करना चाहता हूं, जिसे स्वचालित रूप से एक प्रकार के वर्ग उदाहरण के माध्यम से State मोनैड से स्वचालित रूप से डीकोड किया जा सकता है। तो, get >>= \s -> foo1adic (Constructor (idGet s id1) (idGet s id2)) करने के बजाय, मैं fooVariadic Constructor id1 id2 करना चाहता हूं।

मुझे अब तक जो मिला है, वह है, लिखित हास्केल शैली अगर कोई इसे कॉपी करना चाहता है और इसके साथ गड़बड़ करना चाहता है।

सबसे पहले, बुनियादी पर्यावरण:

> {-# LANGUAGE FlexibleContexts #-} 
> {-# LANGUAGE FlexibleInstances #-} 
> {-# LANGUAGE MultiParamTypeClasses #-} 

> import Control.Monad.Trans.State 

> data Foo = Foo0 
>   | Foo1 Char 
>   | Foo2 Bool Char 
>   | Foo3 Char Bool Char 
> deriving Show 

> type Env = (String,[Bool]) 
> newtype ID a = ID {unID :: Int} 
> deriving Show 

> class InEnv a where envGet :: Env -> ID a -> a 
> instance InEnv Char where envGet (s,_) i = s !! unID i 
> instance InEnv Bool where envGet (_,b) i = b !! unID i 

सुविधा के लिए कुछ परीक्षण डेटा:

> cid :: ID Char 
> cid = ID 1 
> bid :: ID Bool 
> bid = ID 2 
> env :: Env 
> env = ("xy", map (==1) [0,0,1]) 

मैं इस गैर monadic संस्करण, जो केवल पहले के रूप में पर्यावरण लेता है मिल गया है पैरामीटर। यह ठीक काम करता है लेकिन यह काफी नहीं है जो मैं कर रहा हूं। उदाहरण:

$ mkFoo env Foo0 :: Foo 
Foo0 
$ mkFoo env Foo3 cid bid cid :: Foo 
Foo3 'y' True 'y' 

(मैं कार्यात्मक निर्भरता इस्तेमाल कर सकते हैं या परिवारों टाइप :: Foo प्रकार एनोटेशन के लिए जरूरत से छुटकारा पाने के लिए अब मैं इसके बारे में परेशान नहीं कर रहा हूँ के लिए, के बाद से यह नहीं है क्या मैं इच्छुक हूँ। वैसे भी।)

> mkFoo :: VarC a b => Env -> a -> b 
> mkFoo = variadic 
> 
> class VarC r1 r2 where 
> variadic :: Env -> r1 -> r2 
> 
> -- Take the partially applied constructor, turn it into one that takes an ID 
> -- by using the given state. 
> instance (InEnv a, VarC r1 r2) => VarC (a -> r1) (ID a -> r2) where 
> variadic e f = \aid -> variadic e (f (envGet e aid)) 
> 
> instance VarC Foo Foo where 
> variadic _ = id 

अब, मैं एक मोनैड फ़ंक्शन चाहता हूं जो निम्नलिखित मोनैड में चलता है।

> type MyState = State Env 

और मूल रूप से, मुझे नहीं पता कि मुझे आगे कैसे बढ़ना चाहिए। मैंने टाइप क्लास को विभिन्न तरीकों से व्यक्त करने की कोशिश की है (variadicM :: r1 -> r2 और variadicM :: r1 -> MyState r2) लेकिन मैं उदाहरण लिखने में सफल नहीं हुआ हूं। मैंने उपरोक्त गैर-मोनैडिक समाधान को अपनाने का भी प्रयास किया है ताकि मैं किसी भी तरह Env -> Foo के साथ "समाप्त हो जाऊं" जिसे मैं आसानी से MyState Foo में बदल सकता हूं, लेकिन वहां कोई भाग्य नहीं है।

इस प्रकार मेरा सबसे अच्छा प्रयास निम्नानुसार है।

> mkFooM :: VarMC r1 r2 => r1 -> r2 
> mkFooM = variadicM 
> 
> class VarMC r1 r2 where 
> variadicM :: r1 -> r2 
> 
> -- I don't like this instance because it requires doing a "get" at each 
> -- stage. I'd like to do it only once, at the start of the whole computation 
> -- chain (ideally in mkFooM), but I don't know how to tie it all together. 
> instance (InEnv a, VarMC r1 r2) => VarMC (a -> r1) (ID a -> MyState r2) where 
> variadicM f = \aid -> get >>= \e -> return$ variadicM (f (envGet e aid)) 
> 
> instance VarMC Foo Foo where 
> variadicM = id 
> 
> instance VarMC Foo (MyState Foo) where 
> variadicM = return 

यह Foo0 और Foo1 लिए काम करता है, लेकिन नहीं है कि परे:

$ flip evalState env (variadicM Foo1 cid :: MyState Foo) 
Foo1 'y' 
$ flip evalState env (variadicM Foo2 cid bid :: MyState Foo) 

No instance for (VarMC (Bool -> Char -> Foo) 
         (ID Bool -> ID Char -> MyState Foo)) 

(यहाँ मैं एनोटेशन के लिए जरूरत से छुटकारा पाने के लिए करना चाहते हैं, लेकिन वास्तव में इस सूत्रीकरण दो उदाहरणों की जरूरत है Foo के लिए समस्याग्रस्त हो जाता है।)

मैं शिकायत को समझता हूं: मेरे पास केवल एक उदाहरण है जो Bool -> Char -> Foo से ID Bool -> MyState (ID Char -> Foo) पर जाता है। लेकिन मैं उदाहरण नहीं बना सकता क्योंकि यह चाहता है क्योंकि मुझे वहां कहीं MyState की आवश्यकता है ताकि मैं ID Bool को Bool में बदल सकूं।

मुझे नहीं पता कि मैं पूरी तरह से ट्रैक से बाहर हूं या क्या। मुझे पता है कि मैं अपने मूल मुद्दे को हल कर सकता हूं (मैं idGet s समकक्षों के साथ अपने कोड को प्रदूषित नहीं करना चाहता) विभिन्न तरीकों से, जैसे liftA/liftM- विभिन्न प्रकार के आईडी पैरामीटर के लिए स्टाइल फ़ंक्शंस, प्रकारों के साथ (a -> b -> ... -> z -> ret) -> ID a -> ID b -> ... -> ID z -> MyState ret की तरह, लेकिन मैंने इस बारे में सोचने में बहुत अधिक समय बिताया है। :-) मैं जानना चाहता हूं कि यह वैरिएडिक समाधान कैसा दिखना चाहिए।

+0

चूंकि आप स्पष्ट रूप से 'आवेदक' समाधान की तलाश नहीं कर रहे हैं, इसलिए मैं इसे टिप्पणियों में जोड़ रहा हूं: https://gist.github.com/f8e5d1ecf20ea09a8b36 –

उत्तर

2

चेतावनी

अधिमानतः काम के इस प्रकार के लिए variadic कार्यों का उपयोग नहीं करते। आपके पास केवल रचनाकारों की एक सीमित संख्या है, इसलिए स्मार्ट कन्स्ट्रक्टर एक बड़ा सौदा प्रतीत नहीं होता है। ~ 10-20 लाइनों की आपको आवश्यकता होगी एक भिन्न समाधान से बहुत सरल और अधिक रखरखाव योग्य हैं। इसके अलावा एक आवेदक समाधान बहुत कम काम है।

चेतावनी

variadic कार्यों के साथ संयोजन में इकाई/अनुप्रयोगी समस्या है। 'समस्या' भिन्नता वर्ग के लिए उपयोग तर्क तर्क चरण है। बुनियादी वर्ग की तरह

class Variadic f where 
    func :: f 
    -- possibly with extra stuff 

देखो, जहां आप इसे प्रपत्र

instance Variadic BaseType where ... 
instance Variadic f => Variadic (arg -> f) where ... 

कौन सा तोड़ने जब आप monads उपयोग करने के लिए शुरू होगा के उदाहरण होने से variadic होगा। कक्षा परिभाषा में मोनड जोड़ना तर्क विस्तार को रोक देगा (आपको कुछ मोनड एम के लिए :: एम (Arg -> f) मिलेगा। इसे बेस केस में जोड़ना विस्तार में मोनाड का उपयोग करना बंद कर देगा, क्योंकि यह संभव नहीं है (जहां तक ​​मुझे पता है) विस्तार उदाहरण के लिए monadic बाधा जोड़ने के लिए। एक जटिल समाधान के संकेत के लिए पीएस ..

(Env -> Foo) में परिणामस्वरूप एक फ़ंक्शन का उपयोग करने की समाधान दिशा अधिक आशाजनक है। निम्नलिखित कोड को अभी भी :: Foo प्रकार की बाधा की आवश्यकता है और ब्रेवटी के लिए एनवी/आईडी के सरलीकृत संस्करण का उपयोग करता है।

{-# LANGUAGE FlexibleContexts #-} 
{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE MultiParamTypeClasses, TypeFamilies #-} 

module Test where 

data Env = Env 
data ID a = ID 
data Foo 
    = Foo0 
    | Foo1 Char 
    | Foo2 Char Bool 
    | Foo3 Char Bool Char 
    deriving (Eq, Ord, Show) 

class InEnv a where 
    resolve :: Env -> ID a -> a 
instance InEnv Char where 
    resolve _ _ = 'a' 
instance InEnv Bool where 
    resolve _ _ = True 

प्रकार परिवार विस्तार का उपयोग मिलान करने वाले कठोर/बेहतर बनाने के लिए किया जाता है। अब विविध कार्य वर्ग।

class MApp f r where 
    app :: Env -> f -> r 

instance MApp Foo Foo where 
    app _ = id 
instance (MApp r' r, InEnv a, a ~ b) => MApp (a -> r') (ID b -> r) where 
    app env f i = app env . f $ resolve env i 
    -- using a ~ b makes this instance to match more easily and 
    -- then forces a and b to be the same. This prevents ambiguous 
    -- ID instances when not specifying there type. When using type 
    -- signatures on all the ID's you can use 
    -- (MApp r' r, InEnv a) => MApp (a -> r') (ID a -> r) 
    -- as constraint. 

पर्यावरण Env स्पष्ट रूप से पारित कर दिया है, संक्षेप में Reader इकाई monads और (State इकाई संकल्प समारोह एक नए माहौल लौटना चाहिए के लिए) variadic कार्यों के बीच समस्याओं को रोकने पैक नहीं किया गया है। app Env Foo1 ID :: Foo के साथ परीक्षण Foo1 'a' अपेक्षित परिणाम।

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

+0

आपकी अंतर्दृष्टि के लिए धन्यवाद। क्या आपकी 'एमएपीपी कक्षा नहीं है और इसके उदाहरण अनिवार्य रूप से मेरे गैर-मोनैडिक' वीआरसी 'के समान हैं, हालांकि? – Deewiant

+0

@ डिवाइन्ट, वास्तव में है। मुझे लगता है कि यह सबसे अच्छा है जो इस मामले में किया जा सकता है।लेकिन मुझे संदेह है कि कुछ बुरा चाल किए बिना एक मोनैडिक वैरैडिक फ़ंक्शन बनाना संभव है, जिसे मैं चाहें तो एक उत्तर (एक प्रयास) पोस्ट कर सकता हूं। – Laar

+0

कृपया करें! मैं इसका उपयोग करने की योजना नहीं बना रहा हूं, लेकिन मुझे दिलचस्पी है कि किस तरह की चालबाजी की आवश्यकता होगी। – Deewiant