为什么GHC在IO期间出现递归问题?

时间:2011-09-17 17:43:29

标签: haskell

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

失败
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'

我不明白为什么。任何人都可以解释一下吗?

1 个答案:

答案 0 :(得分:10)

首先,您使用randomR错误。它的类型是randomR :: RandomGen g => (a, a) -> g -> (a, g),因此没有涉及monad,但是你将它与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。它已存在于Prelude中,名称为readLn

总而言之,我们得到了这个有效的代码。

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