2012-07-06 10 views
5

मैं एक आईओ मोनैड के साथ एक ऐपस्टेट बनाने के लिए Combine state with IO actions में दी गई सलाह का पालन करने का प्रयास कर रहा हूं। जो मैंने प्राप्त किया है वह यह है:मैं वास्तव में आईओ के साथ एक स्टेटटी मोनड कैसे निष्पादित करूं?

module Main where 

import Control.Monad.State 
import Control.Monad.Trans 

data ST = ST [Integer] deriving (Show) 
type AppState = StateT ST IO 

new = ST [] 

append :: Integer -> State ST() 
append v = state $ \(ST lst) -> ((), ST (lst ++ [v])) 

sumST :: State ST Integer 
sumST = state $ \(ST lst) -> (sum lst, ST lst) 

script = do 
    append 5 
    append 10 
    append 15 
    sumST 

myMain :: AppState() 
myMain = do 
    liftIO $ putStrLn "myMain start" 
    let (res, st) = runState script new 
    liftIO $ putStrLn $ show res 
    liftIO $ putStrLn "myMain stop" 

main = runStateT myMain (ST [15]) 

इसका कुछ हिस्सा मुझे नहीं मिल रहा है। यह मुझे बहुत परेशान करता है कि मेरे पास script और myMainऔरmain है। यह मुझे परेशान करता है कि मुझे runState को myMain के भीतर निष्पादित करना होगा और मुझे अपने मुख्य कार्य में runStateT में प्रारंभिक स्थिति को फ़ीड करना होगा। मैं अपनी "स्क्रिप्ट" रखना चाहता हूं, इसलिए सीधे मेरे मुख्य कार्य में बोलने के लिए, क्योंकि मेरे मेन का पूरा बिंदु परिशिष्ट चलाने और प्रिंट ऑपरेशन के ठीक आगे सीधे चलाने के लिए सक्षम होना है। मुझे लगता है कि मैं इसके बजाय यह करने के लिए, सक्षम होना चाहिए:

myMain :: AppState() 
myMain = do 
    liftIO $ putStrLn "myMain start" 
    append 5 
    append 10 
    append 15 
    r <- sumST 
    liftIO $ putStrLn $ show res 
    liftIO $ putStrLn "myMain stop" 

main = runState myMain 

मैंने सोचा था कि इकाई ट्रांसफार्मर की बात की गई थी इसलिए मैं अपने राज्य इकाई के संचालन में एक समारोह में निष्पादित कर सकते हैं (ऊपर) और में आईओ संचालन उठा वह समारोह यह सब सेट करने का सही तरीका क्या है ताकि मैं संकेतों की परतों में से एक को हटा सकूं?


डैनियल समाधान (जो मैं समाधान चिह्नित कर दिया है) के अलावा, मैं भी कुछ विविधताएं स्थिति पर कुछ प्रकाश डाला सकता है मिल गया है। सबसे पहले, myMain के अंतिम कार्यान्वयन और मुख्य:

myMain :: AppState() 
myMain = do 
    liftIO $ putStrLn "myMain start" 
    append 5 
    append 10 
    append 15 
    res <- sumST 
    liftIO $ putStrLn $ show res 
    liftIO $ putStrLn "myMain stop" 

main = runStateT myMain new 

अब, डैनियल को संलग्न और sumST के विभिन्न कार्यान्वयन, इसके अलावा में:

append :: Integer -> AppState() 
append v = state $ \(ST lst) -> ((), ST (lst ++ [v])) 

sumST :: AppState Integer 
sumST = state $ \(ST lst) -> (sum lst, ST lst) 

और (ध्यान दें कि केवल प्रकार घोषणा परिवर्तन, वास्तव में आप प्रकार घोषणा पूरी तरह से छोड़ सकते हैं!)

append :: MonadState ST m => Integer -> m() 
append v = state $ \(ST lst) -> ((), ST (lst ++ [v])) 

sumST :: MonadState ST m => m Integer 
sumST = state $ \(ST lst) -> (sum lst, ST lst) 

यह मेरे लिए हुआ है कि AppState/StateT इकाई नहीं रूप में ही है मूल राज्य मोनड, और मैं समस्त दोनों कोडिंग और राज्य मोनड के लिए संलग्न था। एक अर्थ में, उन्हें राज्य टी मोनैड में भी उठाया जाना था, हालांकि इनके बारे में सोचने का सही तरीका यह है कि उन्हें मोनड में रन होना चाहिए (इसलिए, runState script new)।

मुझे यकीन नहीं है कि मैं इसे पूरी तरह से प्राप्त करता हूं, लेकिन मैं थोड़ी देर के लिए इसके साथ काम करूंगा, मोनाडस्टेट कोड पढ़ूंगा, और इसके बारे में कुछ लिखूंगा जब यह अंततः मेरे सिर में काम करता है।

+0

आह, 'राज्य' मुझे लगता है की तुलना में अधिक बहुलक है, क्योंकि मेरे पास यह किसी कारण से मेरे सिर में था कि यह कार्य किसी राज्य के कन्स्ट्रक्टर को निर्यात करने के लिए क्षमा नहीं था। टीआईएल! –

उत्तर

10

समस्या यह है कि आपने अपना append और sumST कार्यों को भी मोनोमोर्फिक बनाया है! सीधे state समारोह का उपयोग कर के बजाय, आप अधिक बहुरूपी get और put कार्यों का उपयोग करना चाहिए, ताकि आप उन्हें और अधिक रोमांचक प्रकार

append :: MonadState ST m => Integer -> m() 
append v = do 
    ST lst <- get 
    put (ST (lst ++ [v])) 

sumST :: MonadState ST m => m Integer 
sumST = do 
    ST lst <- get 
    return (sum lst) 

तो फिर तुम लिख सकते हैं दे सकते हैं वास्तव में myMain आप प्रस्तावित (हालांकि आप करेंगे अभी भी main में प्रारंभिक स्थिति देना है)।

एक शैलीगत बात के रूप में, मैं एक नया ST प्रकार को परिभाषित नहीं का प्रस्ताव होगा: वहाँ कि सूचियों के साथ काम बातें करते हैं कार्यों के बहुत सारे हैं, और उन्हें असंभव बना आप के बीच में एक ST निर्माता लगाने से उपयोग करने के लिए और सूचियों हो सकता है कष्टप्रद!आप इसके बजाय अपने राज्य प्रकार के रूप में [Integer] का उपयोग करते हैं, तो आप इस तरह परिभाषाओं कर सकते हैं:

prepend :: MonadState [Integer] m => Integer -> m() 
prepend = modify . (:) 

sumST :: MonadState [Integer] m => m Integer 
sumST = gets sum 

बहुत अच्छा लग रहा है, नहीं? =)

+0

दरअसल, कोडिंग एपेंड और एसएमएसटी समझ में आता है अगर आप इसे अधिक जटिल डेटा प्रकार के लिए सरलीकरण मानते हैं। पसंद (मेरे असली आवेदन में) एसटी {डेटास्टोर :: माईडाटा, event_stream :: [घटनाक्रम]} –

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