为什么surfaceDimensions(出现)到return一个双重封闭的向量(V2 V2 CInt)?

Why does surfaceDimensions (appear) to return a doubly enclosed vector (V2 V2 CInt)?

以下代码无法进行类型检查:

surfDim :: SDL.V2 CInt
surfDim = SDL.surfaceDimensions surfIn

完整错误:

    * Couldn't match type `SDL.V2 CInt' with `CInt'
      Expected type: SDL.V2 CInt
        Actual type: SDL.V2 (SDL.V2 CInt)
    * In the expression: SDL.surfaceDimensions surfIn
      In an equation for `surfDim':
          surfDim = SDL.surfaceDimensions surfIn
      In an equation for `scaleSurface':
          scaleSurface surfIn box scale
            = do surfOut <- createScreenSurface sizeCIntScaled
                 _ <- SDL.surfaceBlitScaled
                        surfIn
                        (Just (fmap fromIntegral boxFinal))
                        surfOut
                        (Just boxFinalScaled)
                 return surfOut
            where
                boxFinal
                  = case box of
                      Just b -> b
                      Nothing
                        -> SDL.Rectangle
                             origin (fmap (fmap fromIntegral) (SDL.surfaceDimensions surfIn))
                boxFinalScaled
                  = fmap (fromIntegral . (* (fromIntegral scale))) boxFinal
                sizeCIntScaled = boxSize boxFinalScaled
                origin :: SDL.Point SDL.V2 Int
                ....
    |
232 |     surfDim = SDL.surfaceDimensions surfIn
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

我看了 the code for surfaceDimensions 后还是一头雾水:

surfaceDimensions :: MonadIO m => Surface -> m (V2 CInt)
surfaceDimensions (Surface s _) = liftIO $ (V2 <$> Raw.surfaceW <*> Raw.surfaceH) <$> peek s

自从 V2(对我来说是正确的)doesn't seem to have an instance of MonadIO, I don't know why this is happening, but my best guess is that it is satisfying it and I don't know why or how (probably due to my incomplete understanding of liftIO)。

这里有两个问题 - 让您的代码编译,并从 GHC 获得更好的错误消息。如果您 post 更多代码,我们可以在第一个方面提供更多帮助。

如果您不使用 surfDim 的类型签名,您会得到更有用的错误吗?

V2 CIntV2 (V2 CInt) 都不是可能的类型。你需要像 IO (V2 CInt) 这样的东西来满足 MonadIO m

GHC 尝试为 m 选择一个类型,在 V2 CInt 类型 sig 的指导下,并决定 m ~ V2。不幸的是,这个选择并没有产生有用的错误信息。如果您使用建议的类型 sig 作为 "Actual type",我相信 GHC 将深入到类型检查过程中,以注意到没有实例 MonadIO V2,正如您指出的那样。

我不知道是否有可能让 GHC 始终如一地就此类事情提供更好的建议。当我被类型错误难倒时,我喜欢尝试添加更多类型信号或删除一些。通常有不同的错误消息,对我来说,一个可能比另一个更有意义。

没有。您 运行 与类型检查器在报告错误消息之前尽可能统一的趋势相冲突。在这种情况下,类型检查器认为,"Oh, this expression has type m something, and I want a V2 something. So m must be V2." 它推迟到稍后检查 m = V2 是否满足 MonadIO 约束。在它到达那里之前,它查看了顶级类型,并意识到它不能统一 V2 CIntCInt,所以它在检查 V2 是否是一个 monad 之前就失败了.

作为一个人,您可能会通过支持和质疑您的哪些假设是错误的来回应错误。类型检查器根本不这样做。相反,它假设它的所有假设都很好,直到无法继续进行为止。然后它抱怨最后一步。到那时它已经掉进了一些奇怪的兔子洞是相当普遍的,这就是为什么 Haskell 类型错误是出了名的糟糕,尤其是当涉及类型 类 时。

现在,这是真正的错误。您需要计算 IO monad(或 MonadIO 的其他实例)中的维度。这意味着你需要做:

surfDim <- SDL.surfaceDimensions surfIn

在一些适当的 do 块中。你不能把它变成一个普通的方程式。