2011-09-17 91 views
1
guessOneToTen :: IO() 
guessOneToTen = 
    forever (do 
     number <- newNumber 
     guessed <- firstPrompt 
     untilM (== number) (const prompt) guessed 
     correct) 
    where 
     newNumber = newStdGen >>= randomR (1, 10) >>= return . fst 
     readLine = getLine >>= return . read 
     firstPrompt = putStr "Guess what number I am thinking of: " >> readLine 
     prompt = putStr "Sorry try again: " >> readLine 
     correct = putStrLn "You guessed correct!" 
     untilM :: (Monad m) => (a -> Bool) -> (a -> m a) -> a 
     untilM p f x 
      | p x = return() 
      | otherwise = f x >>= untilM p f 

這種失敗爲什麼GHC在IO過程中遇到遞歸問題?

baby.hs:804:43: 
    Occurs check: cannot construct the infinite type: t0 = t0 -> IO a0 
    In the third argument of `untilM', namely `guessed' 
    In a stmt of a 'do' expression: 
     untilM (== number) (const prompt) guessed 
    In the first argument of `forever', namely 
     `(do { number <- newNumber; 
      guessed <- firstPrompt; 
      untilM (== number) (const prompt) guessed; 
      correct })' 

baby.hs:807:35: 
    Couldn't match expected type `IO a0' 
       with actual type `(a1, StdGen)' 
    Expected type: StdGen -> IO a0 
     Actual type: StdGen -> (a1, StdGen) 
    In the return type of a call of `randomR' 
    In the second argument of `(>>=)', namely `randomR (1, 10)' 

baby.hs:813:9: 
    Couldn't match type `a' with `a -> m()' 
     `a' is a rigid type variable bound by 
      the type signature for 
      untilM :: Monad m => (a -> Bool) -> (a -> m a) -> a 
      at baby.hs:813:9 
    The equation(s) for `untilM' have three arguments, 
    but its type `(a -> Bool) -> (a -> m a) -> a' has only two 
    In an equation for `guessOneToTen': 
     guessOneToTen 
      = forever 
       (do { number <- newNumber; 
        guessed <- firstPrompt; 
        untilM (== number) (const prompt) guessed; 
        correct }) 
      where 
       newNumber = newStdGen >>= randomR (1, 10) >>= return . fst 
       readLine = getLine >>= return . read 
       firstPrompt 
       = putStr "Guess what number I am thinking of: " >> readLine 
       prompt = putStr "Sorry try again: " >> readLine 
       .... 

baby.hs:815:42: 
    Couldn't match type `a' with `a -> m()' 
     `a' is a rigid type variable bound by 
      the type signature for 
      untilM :: Monad m => (a -> Bool) -> (a -> m a) -> a 
      at baby.hs:813:9 
    Expected type: (a -> m()) -> Bool 
     Actual type: a -> Bool 
    In the first argument of `untilM', namely `p' 
    In the second argument of `(>>=)', namely `untilM p f' 

baby.hs:815:44: 
    Couldn't match type `a' with `a -> m()' 
     `a' is a rigid type variable bound by 
      the type signature for 
      untilM :: Monad m => (a -> Bool) -> (a -> m a) -> a 
      at baby.hs:813:9 
    Expected type: (a -> m()) -> m (a -> m()) 
     Actual type: a -> m a 
    In the second argument of `untilM', namely `f' 
    In the second argument of `(>>=)', namely `untilM p f' 

baby.hs:815:44: 
    Couldn't match type `a' with `a -> m()' 
     `a' is a rigid type variable bound by 
      the type signature for 
      untilM :: Monad m => (a -> Bool) -> (a -> m a) -> a 
      at baby.hs:813:9 
    Expected type: (a -> m()) -> m (a -> m()) 
     Actual type: a -> m a 
    In the second argument of `untilM', namely `f' 
    In the second argument of `(>>=)', namely `untilM p f' 

我不明白爲什麼。任何人都可以點亮一下嗎?

回答

10

首先,您正在使用randomR錯誤。它的類型是randomR :: RandomGen g => (a, a) -> g -> (a, g),所以沒有monads參與,但是您將其與monadic綁定運算符(>>=)一起使用。你還必須添加一個類型簽名來指定你想要的隨機數。

我們可以將其更改爲此。

newNumber = newStdGen >>= return . fst . randomR (1, 10) :: IO Int 

但是,每次都不需要新的發生器。我們可以通過使用由IO提供的那個來簡化這個。

newNumber = randomRIO (1, 10) :: IO Int 

其次,您的untilM類型簽名是錯誤的。我們可以直接忽略它,讓編譯器推斷出正確的類型,在這種情況下是

untilM :: Monad m => (a -> Bool) -> (a -> m a) -> a -> m() 

而且,你不必定義readLine。它已經存在於名爲readLn的Prelude中。

把這一切放在一起,我們得到這個工作代碼。

guessOneToTen :: IO() 
guessOneToTen = 
    forever (do 
     number <- newNumber 
     guessed <- firstPrompt 
     untilM (== number) (const prompt) guessed 
     correct) 
    where 
     newNumber = randomRIO (1, 10) :: IO Int 
     firstPrompt = putStr "Guess what number I am thinking of: " >> readLn 
     prompt = putStr "Sorry try again: " >> readLn 
     correct = putStrLn "You guessed correct!" 
     untilM :: (Monad m) => (a -> Bool) -> (a -> m a) -> a -> m() 
     untilM p f x 
      | p x = return() 
      | otherwise = f x >>= untilM p f 
+0

我做了所有這三項修改。它現在像一個魅力。 –