2012-06-06 20 views
10

में मोनैड का मिश्रण मैं एक स्पाइडर सॉलिटेयर प्लेयर को हास्केल सीखने के अभ्यास के रूप में लिखने की कोशिश कर रहा हूं।हास्केल

मेरे main समारोह (mapM का प्रयोग करके) प्रत्येक खेल के लिए एक बार एक playGame फ़ंक्शन को कॉल करें, खेल संख्या और एक यादृच्छिक जनरेटर (StdGen) में गुजर जाएगा। playGame फ़ंक्शन को Control.Monad.State मोनैड और आईओ मोनैड वापस करना चाहिए जिसमें String गेम टेबलू और Bool दिखा रहा है, यह दर्शाता है कि गेम जीता या खो गया था या नहीं।

वापसी मूल्य के लिए IO मोनैड के साथ State मोनैड को मैं कैसे जोड़ूं? 'PlayGame के लिए प्रकार की घोषणा क्या होनी चाहिए?

playGame :: Int -> StdGen a -> State IO (String, Bool) 

State IO (String, Bool) सही है? यदि नहीं, तो यह क्या होना चाहिए?

main में, मैं

do 
    -- get the number of games from the command line (already written) 
    results <- mapM (\game -> playGame game getStdGen) [1..numberOfGames] 

इस है सही तरीका का उपयोग कर playGame कॉल करने के लिए पर योजना?

+0

आप [MonadRandom] (http://hackage.haskell.org/package/MonadRandom) पैकेज से 'RandT' का भी आनंद ले सकते हैं। –

उत्तर

12

तुम क्या चाहते StateT s IO (String, Bool), जहां StateT (mtl पैकेज से) दोनों Control.Monad.State द्वारा प्रदान की जाती है और Control.Monad.Trans.State (transformers पैकेज से) है।

इस सामान्य घटना को एक मोनड ट्रांसफार्मर कहा जाता है, और आप उन्हें Monad Transformers, Step by Step में एक महान परिचय पढ़ सकते हैं।

उन्हें परिभाषित करने के दो दृष्टिकोण हैं। उनमें से एक transformers पैकेज में पाया गया है जो उन्हें लागू करने के लिए MonadTrans कक्षा का उपयोग करता है। दूसरा दृष्टिकोण mtl वर्ग में पाया जाता है और प्रत्येक मोनड के लिए एक अलग प्रकार की कक्षा का उपयोग करता है।

transformers दृष्टिकोण का लाभ एक ही प्रकार स्तरीय सब कुछ को लागू करने का प्रयोग होता है (here पाया जाता है):

:

class MonadTrans t where 
    lift :: Monad m => m a -> t m a 

lift दो अच्छा गुण जो MonadTrans के किसी भी मामले को पूरा करना चाहिए है

(lift .) return = return 
(lift .) f >=> (lift .) g = (lift .) (f >=> g) 

ये छिपाने में मज़ेदार कानून हैं, जहां (lift .) = fmap, return = id और (>=>) = (.)

mtl प्रकार स्तरीय दृष्टिकोण के अपने फायदे भी है, और कुछ बातें केवल सफाई से हो mtl प्रकार-वर्गों का उपयोग हल कर सकते हैं, हालांकि नुकसान तो प्रत्येक mtl प्रकार स्तरीय कानूनों आप के स्वयं के सेट है कि याद रखने के लिए जब इसके लिए उदाहरण लागू करें। उदाहरण के लिए, MonadError प्रकार स्तरीय (here पाया जाता है) के रूप में परिभाषित किया गया है:

class Monad m => MonadError e m | m -> e where 
    throwError :: e -> m a 
    catchError :: m a -> (e -> m a) -> m a 

इस वर्ग भी ससुराल वालों के साथ आता है:

m `catchError` throwError = m 
(throwError e) `catchError` f = f e 
(m `catchError` f) `catchError` g = m `catchError` (\e -> f e `catchError` g) 

ये भेष में सिर्फ इकाई कानूनों, जहां throwError = return और कर रहे हैं catchError = (>>=) (और मोनड कानून छद्म श्रेणी में कानून कानून हैं, जहां return = id और (>=>) = (.))।

अपने विशिष्ट समस्या के लिए, जिस तरह से आप अपने प्रोग्राम लिखने होगा ही होगा:

do 
    -- get the number of games from the command line (already written) 
    results <- mapM (\game -> playGame game getStdGen) [1..numberOfGames] 

... लेकिन जब आप अपने playGame समारोह लिखने यह या तो दिखाई देगा:

-- transformers approach :: (Num s) => StateT s IO() 
do x <- get 
    y <- lift $ someIOAction 
    put $ x + y 

-- mtl approach :: (Num s, MonadState s m, MonadIO m) => m() 
do x <- get 
    y <- liftIO $ someIOAction 
    put $ x + y 

जब आप एक से अधिक मोनैड ट्रांसफॉर्मर को ढेर करना शुरू करते हैं तो उन दृष्टिकोणों के बीच और अंतर होते हैं, लेकिन मुझे लगता है कि यह अभी के लिए एक अच्छी शुरुआत है।

+0

बहुत अच्छा और पूरा जवाब। धन्यवाद। – Ralph

+1

'स्टेटटी आईओ (स्ट्रिंग, बूल) 'गलत है - एक तरह का मेल नहीं है। यह राज्य के प्रकार के साथ 'स्टेटटी एस एम 'है और एक मोनैड है, और परिणाम प्रकार है। –

+0

इसके अलावा, 'एमटीएल' दृष्टिकोण और' ट्रांसफार्मर 'दृष्टिकोण वास्तव में एक ही काम करने के अलग-अलग तरीके नहीं हैं -' मोनाड एरर '' मोनाडट्रान 'से एक अलग लक्ष्य प्राप्त करता है। और कानून छिपाने में 'मोनाद' कानून नहीं हैं - वे बहुत * समान * हैं, लेकिन शामिल चीजों के प्रकार, और उनका अर्थ अलग है। मेरा मतलब है, वे मूल रूप से एक इकाई कानून और एक सहयोगी कानून हैं, लेकिन पूरी तरह से अलग संचालन के संबंध में। –

8

State एक मोनड है, और IO एक मोनड है। जो आप स्क्रैच से लिखने की कोशिश कर रहे हैं उसे "मोनैड ट्रांसफॉर्मर" कहा जाता है, और हास्केल मानक लाइब्रेरी पहले से ही परिभाषित करता है कि आपको क्या चाहिए।

राज्य मोनैड ट्रांसफॉर्मर StateT पर एक नज़र डालें: इसमें एक पैरामीटर है जो आंतरिक मोनैड है जिसे आप State में लपेटना चाहते हैं।

प्रत्येक मोनैड ट्रांसफार्मर टाइपक्लास का एक गुच्छा लागू करता है, जैसे प्रत्येक उदाहरण के लिए, ट्रांसफ़ॉर्मर हर बार इसके साथ संबंधित होता है (उदाहरण के लिए राज्य ट्रांसफॉर्मर केवल राज्य से संबंधित कार्यों को सीधे संभाल सकता है), या यह कॉल को प्रसारित करता है आंतरिक मोनैड को इस तरह से कि जब आप अपने इच्छित ट्रांसफार्मर को ढेर कर सकते हैं, और उनमें से सभी की सुविधाओं तक पहुंचने के लिए एक समान इंटरफेस है। यह chain of responsibility का एक प्रकार है, यदि आप इसे इस तरह देखना चाहते हैं।

यदि आप hackage पर देखते हैं, या स्टैक ओवरफ़्लो या Google पर त्वरित खोज करते हैं तो आपको StateT के उपयोग के कई उदाहरण मिलेंगे।

संपादित करें: एक और दिलचस्प पढ़ने Monad Transformers Explained है।

+1

मुझे पसंद है कि कितने बार हास्केल सीखने वाले पहिये को रेडस्कोवर करते हैं ... वास्तव में समस्या का हल ढूंढना और यह पता लगाना बहुत अच्छा है कि यह एक सामान्य डिजाइन पैटर्न है। – fuz

+1

@FUZxxl: हाँ, यह वास्तव में है :) –

2

ठीक है, कुछ बातें यहाँ स्पष्ट करने के लिए:

  • आप "एक इकाई लौटने" कर सकते हैं। एक मोनड प्रकार टाइप करें, एक प्रकार का मूल्य नहीं (सटीक होना, एक मोनड प्रकार कन्स्ट्रक्टर है जिसमें Monad वर्ग का उदाहरण है)। मुझे पता है कि यह पैडेंटिक लगता है, लेकिन यह आपके सिर में चीजों और प्रकारों के बीच भेद को हल करने में मदद कर सकता है, जो महत्वपूर्ण है।
  • ध्यान दें कि आप State के साथ कुछ भी नहीं कर सकते हैं, इसके बिना असंभव है, इसलिए यदि आप इसका उपयोग कैसे करें इसके बारे में उलझन में हैं, तो आपको यह महसूस करने की आवश्यकता नहीं है! अक्सर, मैं केवल सामान्य फ़ंक्शन प्रकार लिखता हूं, और फिर यदि मुझे लगता है कि मेरे पास Thing -> (Thing, a) जैसे आकार के बहुत सारे फ़ंक्शन हैं, तो मैं "आह, यह State जैसा दिखता हूं, शायद इसे State Thing a पर सरलीकृत किया जा सकता है"। सादे कार्यों के साथ समझना और काम करना State या उसके दोस्तों का उपयोग करने के लिए सड़क पर एक महत्वपूर्ण पहला कदम है।
  • IO, दूसरी तरफ, यह एकमात्र चीज है जो अपना काम कर सकती है। लेकिन नाम playGame तुरंत मुझे बाहर निकलने वाला कुछ नहीं है जो I/O करने की आवश्यकता है।विशेष रूप से, यदि आप केवल की आवश्यकता है (छद्म-) यादृच्छिक संख्या, तो आप IO के बिना ऐसा कर सकते हैं। जैसा कि एक टिप्पणीकर्ता ने इंगित किया है, MonadRandom यह आसान बनाने के लिए बहुत अच्छा है, लेकिन फिर आप केवल System.Random से लेने और लौटने वाले शुद्ध कार्यों का उपयोग कर सकते हैं। आपको बस यह सुनिश्चित करना होगा कि आप अपने बीज (StdGen) को सही ढंग से थ्रेड करें (यह स्वचालित रूप से ऐसा कर रहा था क्यों State का आविष्कार किया गया था; आप इसे बिना प्रोग्राम के प्रयास करने के बाद बेहतर समझ सकते हैं!)
  • अंत में, आप getStdGen सही ढंग से उपयोग नहीं कर रहा है। यह IO एक्शन है, इसलिएमें इसका उपयोग करने से पहले के साथ अपने परिणाम को बाध्य करने की आवश्यकता है (तकनीकी रूप से, आप की आवश्यकता नहीं है, आपके पास बहुत सारे विकल्प हैं, लेकिन यह लगभग निश्चित रूप से आप क्या करना चाहते हैं) । कुछ ऐसा:

    do 
        seed <- getStdGen 
        results <- mapM (\game -> playGame game seed) [1..numberOfGames] 
    

    यहां playGame :: Integer -> StdGen -> IO (String, Bool)। हालांकि, ध्यान दें कि आप प्रत्येक playGame पर समान यादृच्छिक बीज पारित कर रहे हैं, जो आप चाहते हैं या नहीं भी हो सकता है। यदि यह नहीं है, तो, आप प्रत्येक playGame से बीज वापस कर सकते हैं, जब आप इसके साथ किए गए थे, अगली बार पास करने के लिए, या newStdGen के साथ बार-बार नए बीज प्राप्त कर सकते हैं (यदि आप playGame के अंदर से कर सकते हैं, तो आप निर्णय लेते हैं इसे IO में रखें)।

वैसे भी, यह एक बहुत ही संरचित जवाब है, जिसके लिए मैं माफी माँगता हूँ नहीं किया गया है, लेकिन मुझे आशा है कि यह आप कुछ के बारे में सोचना देता है।

+0

"* एक मोनड एक प्रकार का * प्रकार है, * एक प्रकार का मूल्य नहीं है। *" क्या यह कहना उचित नहीं होगा कि यह वास्तव में एक प्रकार का कन्स्ट्रक्टर है? – Ashe

+1

हां, एक मोनड एक प्रकार का प्रकार का कन्स्ट्रक्टर है, लेकिन मैं बहुत तकनीकी नहीं लगाना चाहता था - मैं सिर्फ उस पर जोर देना चाहता था कि "मोनैड प्रकार की दुनिया से संबंधित हैं"। मैं इसे और अधिक सटीक होने के लिए संपादित कर दूंगा। –