gpt4 book ai didi

Haskell:将函数作为参数传递时出现刚性类型变量错误

转载 作者:行者123 更新时间:2023-12-02 10:09:11 25 4
gpt4 key购买 nike

GHC 说我的函数太笼统,无法作为参数传递。

这是重现错误的简化版本:

data Action m a = SomeAction (m a)


runAction :: Action m a -> m a
runAction (SomeAction ma) = ma

-- Errors in here
actionFile :: (Action IO a -> IO a) -> String -> IO ()
actionFile actionFunc fileName = do
actionFunc $ SomeAction $ readFile fileName
actionFunc $ SomeAction $ putStrLn fileName


main :: IO ()
main =
actionFile runAction "Some Name.txt"

错误内容如下:

 • Couldn't match type ‘a’ with ‘()’
‘a’ is a rigid type variable bound by
the type signature for:
actionFile :: forall a. (Action IO a -> IO a) -> String -> IO ()
at src/Lib.hs:11:15
Expected type: Action IO a
Actual type: Action IO ()

编译器希望我的类型签名更加具体,但我不能,因为我需要将参数函数与不同类型的参数一起使用。就像在我的示例中一样,我向它传递了一个 Action IO () 和一个 Action IO String

如果我用 (Action IO a -> IO a) -> String -> IO () 替换 (Action IO () -> IO ()) -> String -> IO (),就像编译器所要求的那样,调用时会出现 readFile 错误,因为它输出一个 IO String

为什么会发生这种情况?我应该怎么做才能将此函数作为参数传递?

我知道,如果我只在 actionFile 函数中使用 runAction ,一切都会起作用,但在我的真实代码中 runAction 是部分应用的函数是根据 IO 计算结果构建的,因此在编译时不可用。

最佳答案

这是一个量词问题。类型

actionFile :: (Action IO a -> IO a) -> String -> IO ()

意味着,正如 GHC 错误所报告的那样,

actionFile :: forall a. (Action IO a -> IO a) -> String -> IO ()

其中声明以下内容:

  • 调用者必须选择类型a
  • 调用者必须提供一个函数g::Action IO a -> IO a
  • 调用者必须提供一个字符串
  • 最后,actionFile 必须使用 IO () 进行响应

请注意,a 是由调用者选择的,而不是由 actionFile 选择的。从actionFile的角度来看,这样的类型变量绑定(bind)到一个固定的未知类型,由其他人选择:这就是GHC在错误中提到的“刚性”类型变量。

但是,actionFile 正在调用 g 并传递 Action IO () 参数(因为 putStrLn)。这意味着 actionFile 要选择 a = ()。由于调用者可以选择不同的 a,因此会引发类型错误。

此外,actionFile 还想调用 g 并传递 Action IO String 参数(因为 readFile) ,所以我们还要选择a = String。这意味着g必须接受我们希望的任何a选择。

正如 Alexis King 所提到的,解决方案可能是移动量词并使用 2 级类型:

actionFile :: (forall a. Action IO a -> IO a) -> String -> IO ()

这种新类型意味着:

  • 调用者必须提供一个函数g::forall a。 Action IO a -> IO a
    • g 的调用者(即 actionFile)必须选择 a
    • g 的调用者(即 actionFile)必须提供 Action IO a
    • 最后,g 必须提供一个IO a
  • 调用者必须提供一个字符串
  • 最后,actionFile 必须使用 IO () 进行响应

这使得 actionFile 可以根据需要选择 a

关于Haskell:将函数作为参数传递时出现刚性类型变量错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45296481/

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