2017-05-04 37 views
0

我想在Haskell中寫一個非常簡單的遊戲。 我有這個循環,這是最後一部分,我不知道該怎麼做。當然,這是一個循環,所以它必須自行運行。Haskell中IO與遊戲循環

gameLoop :: User -> IO() 
gameLoop initUser = displayPosition initUser >> gameLoop (applyAction initUser) 
    where 
    applyAction :: UserAction 
    applyAction = maybe id (unsafePerformIO (fmap getUserAction getLine)) 

getUserAction :: String -> Maybe UserAction是查找地圖中的一個字符串,並返回一個UserAction :: User -> User的功能。然後,我做了一些難看的拆包(unsafePerformIO),我不知道這些拆包。

我認爲這應該運行,因爲我的類型似乎是正確的,但它仍然沒有。

它說:

maybe is applied to too few arguments AND 
couldn't match a1 -> a0 -> a0 with actual type Maybe UserAction, because unsafePerformIO is applied to too few arguments. 

我不明白這些錯誤。任何人都可以解釋如何解決這些最後的問題,或者如何擺脫unsafePerformIO

+0

我建議你看看從'Control.Monad'永遠''。 –

+0

這不是一遍又一遍地執行相同的monad嗎?我想傳遞initUser。 – hgiesel

回答

1

爲了不使用unsafePerformIO,請使用IO。試試這個:

getUserAction <$> getLine :: IO (Maybe UserAction) 

<$>fmap。這是一個獲取用戶操作以從用戶執行的IO操作。然後,使用fromMaybe設置deafult值(在這種情況下id)來轉換Maybe UserActionUserAction

getAction :: IO UserAction 
getAction = fromMaybe id . getUserAction <$> getLine 

注意a . b <$> c(a . b) <$> c,不a . (b <$> c)

現在你可以在你的主循環一次性使用此功能:

gameLoop :: User -> IO() 
gameLoop initUser = displayPosition initUser >> getAction >>= \userAction -> gameLoop (userAction initUser) 

或者使用do -notation同樣的事情:

gameLoop :: User -> IO() 
gameLoop initUser = do 
    displayPosition initUser 
    userAction <- getAction 
    gameLoop (userAction initUser) 
3

樣板講座

首先,假裝unsafePerformIO不存在。其次,下一次請提出一個更完整的代碼片段,我會盡力回答,但會一直做出假設。

步行通過的

您呈現:

gameLoop :: User -> IO() 
gameLoop initUser = displayPosition initUser >> gameLoop (applyAction initUser) 

因此,看來你必須有一些定義displayPosition :: User -> IO()。然後下面你使用UserAction這似乎是type UserAction = User -> User

applyAction :: UserAction 

現在你突然意識到你不想User -> User型,而是有這個IO你想要做的,產生User -> IO User類型:

applyAction = maybe id (unsafePerformIO (fmap getUserAction getLine)) 

不是使IO神奇,和完全不安全,消失,你可以可以定義:

applyAction :: User -> IO User 
applyAction previousUser = 
    do ln <- getLine 
     case getUserAction ln of 
      Nothing -> -- You never said what to do here. 
         -- This is the same logical issue as the missing 
         -- argument to your call to `maybe` above. 
         return previousUser -- XXX do something correct! 
      Just act -> return act 

回過頭來看看這個gameLoop,類型已經改變和W e不能使用applyAction initUser :: IO User,期望值爲:: User。我們可以,但是,使用一元綁定或做標記法:

gameLoop initUser = 
     do displayPosition initUser 
     newUser <- applyAction initUser 
     gameLoop newUser 

這只是語法糖:

gameLoop initUser = displayPosition initUser >> applyAction initUser >>= \newUser -> gameLoop newUser 

或者乾脆:

gameLoop initUser = displayPosition initUser >> applyAction initUser >>= gameLoop 

更多重複寫

這是一個解決方案,但它會很好,保持applyAction函數無效(無IO),所以你可以測試它,更容易重構該程序。而不是在那裏得到一條線那麼我們如何得到一條線在循環中,並通過它:

gameLoop initUser = 
     do displayPosition initUser 
     command <- getLine 
     newUser <- applyAction command initUser 
     gameLoop newUser 

applyAction :: String -> User -> User 
applyAction cmd oldState = maybe oldState id (getUserAction ln)