2017-05-04 13 views
0

私はハスケルで非常に簡単なゲームを書こうとしています。 私は最後の部分であり、私は実際に何をすべきかわからないこのループを持っています。もちろん、それはループなので、実行する必要があります。ハスケルで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」から「forever」を見ることをお勧めします。 –

+0

それは同じモナドを何度も繰り返し実行していませんか?私はinitUserに沿って渡したい。 – hgiesel

答えて

1

unsafePerformIOを使用しない場合は、IOを使用してください。これを試してみてください:

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

<$>fmapです。これは、ユーザアクションをユーザから取得するIOアクションです。 a . b <$> c(a . b) <$> c、ないa . (b <$> c)ある

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

注こと:次に、あなたのMaybe UserActionUserActionに変換するには(この場合はidで)deafult値を設定するためにfromMaybeを使用しています。

今、あなたはあなたのメインループ内の1つのショットでは、この機能を使用することができます。

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()のためのいくつかの定義を持っている必要がありそうです。次に、type UserAction = User -> Userと思われるUserActionを使用します。、

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

代わりの魔法のIOを作る:

applyAction :: UserAction 

は今、あなたは突然 User -> IO User種類をもたらす、あなたは User -> Userタイプをしたいが、代わりにあなたがしたいのですが、この IOありません実現しますそして完全に危険な状態で、ユーザーが定義することができ可能性が消える: gameLoopに戻って

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 

、種類を変更してwをしています期待値が:: Userの場合、applyAction initUser :: IO 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 

もっと書き換え

それは1つの解決策でしたが、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) 
関連する問題