gpt4 book ai didi

haskell - 如何理解 Haskell 编译器消息

转载 作者:行者123 更新时间:2023-12-04 16:17:48 25 4
gpt4 key购买 nike

再会。

这是简单的“猜数”片段,它包含单个错误,但编译器
很难理解出了什么问题:

import System.Random
import Control.Monad
import Control.Monad.Cont

main = do
rn <- randomRIO (1,10) :: IO Int
runContT (callCC $ \k -> forever $ do
lift $ putStr "Your number:"
n <- lift (fmap read getLine)
when (n == rn) $ k
lift $ putStrLn $ if (n > rn)
then "Too much"
else "Too little") (return)
putStrLn $ "Good guess! " ++ (show rn)

GHC 给出错误:
> simple.hs:11:21:
> Couldn't match expected type `()' against inferred type `m b'
> Expected type: a -> ()
> Inferred type: a -> m b
> In the second argument of `($)', namely `k'
> In a stmt of a 'do' expression: when (n == rn) $ k

它让我很困惑,它告诉了一些预期的类型'()',但是如何
发现这个“东西”是谁?是“k”吗?似乎不是这样。
如果我们交换预期和推断,它看起来更理智,但现在看起来,它非常令人困惑。我的问题是:如何找出原因并修复此错误?

最佳答案

要了解这些类型,最好看一下周围的函数。

错误提到了变量 k ,它首先出现在表达式 callCC $ \k -> forever ... .我们可以通过查看 callCC 的类型来获得 k 的类型。 :

callCC :: MonadCont m => ((a -> m b) -> m a) -> m a

从中可以看出 k类型为 a -> m b .请注意,作为 b没有在该函数的其他任何地方使用,它的类型无关紧要,将由使用该函数的上下文确定。
k$ 之后的 when 表达式中使用(实际上并不需要)。 when 的类型是:
 when :: Monad m => Bool -> m () -> m ()

注意第二个参数需要 m () ,但您传入的是类型为 a -> m b 的 k (因为 b 没关系,它可以匹配 () 。所以显然需要给 k 一些参数。要弄清楚 a 是什么,我们回顾一下 callCC 的定义。那个 arg 是值 forever $ do ... 在您的程序中。

看forever的类型:
forever :: Monad m => m a -> m b

它需要一次单子(monad)计算 m a ,并因此返回另一个一元计算 m b .注意 b没有出现在 forever 的参数中.这意味着类型由调用它的上下文确定(如 read "3" 可以是 DoubleInt 类型,具体取决于它所在的表达式)。这由 runContT 决定:
runContT :: ContT r m a -> (a -> m r) -> m r

如果匹配 runContT 中的类型变量, callCCforever ,您会注意到 bforever对应于 arunContT . a用于 runContT 的第二个参数,在您的程序中是 return . return有类型 a -> m a ,所以 a 的类型与 r 相同在你的程序中。 r出现在输出 m r .
runContT表达式在 do 上下文中,没有任何绑定(bind) ( <-)。所以你的代码相当于这个:
main = do
rn <- randomRIO (1,10) :: IO Int
runContT (callCC ....) (return) >> (putStrLn $ "Good guess! " ++ (show rn))

通过查看 >> 的类型,谜底最终将被解开。 :
(>>) :: Monad m => m a -> m b -> m b
>>丢弃传递给它的第一个单子(monad)计算的值(即 runContT 表达式)。所以计算返回的值实际上并不重要(注意 a 是如何出现在 >> 函数的结果中的)。如果你通过这个解释遵循这个结果,你会意识到变量传递给了 k。其实没关系!如果您将任何内容传递给它,该功能将正常工作:
import System.Random
import Control.Monad
import Control.Monad.Cont

main = do
rn <- randomRIO (1,10) :: IO Int
runContT (callCC $ \k -> forever $ do
lift $ putStr "Your number:"
n <- lift (fmap read getLine)
when (n == rn) $ k (Just ("Seriously anything works here", 42, [42..]))
lift $ putStrLn $ if (n > rn)
then "Too much"
else "Too little") (return)
putStrLn $ "Good guess! " ++ (show rn)

所以这是一个非常困难的例子,我理解你为什么不遵循它。不过,您确实会通过经验变得更好。此外,延续单子(monad)是相当先进和复杂的haskell。

关于haskell - 如何理解 Haskell 编译器消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5747181/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com