2017-05-04 7 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'에서 '영원히'볼 것을 제안합니다. –

+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 A와 변환 (이 경우 id에서)를 deafult 값을 설정 fromMaybe를 사용합니다.

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

또는 같은 일에 대한 do 표기법을 사용합니다 : 이제

당신은 당신의 메인 루프에서 한 번에이 기능을 사용할 수 있습니다

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 = displayPosition initUser >> applyAction initUser >>= \newUser -> gameLoop newUser 

또는 :

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

더 다시 쓰기

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

이가 단지 문법 설탕입니다 : 우리는, 그러나, 모나드 바인딩을 사용하거나 할-표기 할 수

그것은 하나의 해결책 이었지만 01을 유지하는 것이 좋습니다.함수는 효과가 없으므로 (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)