2017-05-04 41 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)