Mar*_*oni 2 haskell types hindley-milner
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"
Run Code Online (Sandbox Code Playgroud)
这就是错误所说的:
• 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 ()
Run Code Online (Sandbox Code Playgroud)
编译器希望我在我的类型签名中更具体,但我不能,因为我需要使用不同类型的参数的参数函数.就像在我的例子中我传递它Action IO ()和一个Action IO String.
如果我取代(Action IO a -> IO a) -> String -> IO ()了(Action IO () -> IO ()) -> String -> IO (),像编译器要求,与调用readFile错误,因为它输出IO String.
为什么会发生这种情况,我该怎么做才能将此函数作为参数传递?
我知道如果我只是runAction在我的actionFile函数内部使用一切都会工作,但在我的实际代码中runAction是一个部分应用的函数,它是从IO计算的结果构建的,所以它在编译时不可用.
这是一个量词问题.类型
actionFile :: (Action IO a -> IO a) -> String -> IO ()
Run Code Online (Sandbox Code Playgroud)
意味着,如GHC错误所报告的那样,
actionFile :: forall a. (Action IO a -> IO a) -> String -> IO ()
Run Code Online (Sandbox Code Playgroud)
其中陈述如下:
ag :: Action IO a -> IO aStringactionFile必须回答IO ()请注意,a调用者选择,而不是actionFile.从这个角度来看actionFile,这种类型变量被一个固定的未知类型绑定,由其他人选择:这是GHC在错误中提到的"刚性"类型变量.
但是,actionFile正在调用g传递Action IO ()参数(因为putStrLn).这意味着actionFile想要选择a = ().由于调用者可以选择不同a的类型,因此会引发类型错误.
此外,actionFile还想调用g传递Action IO String参数(因为readFile),所以我们也想选择a = String.这意味着g必须接受a我们所希望的任何选择.
正如Alexis King所提到的,解决方案可能是移动量词并使用rank-2类型:
actionFile :: (forall a. Action IO a -> IO a) -> String -> IO ()
Run Code Online (Sandbox Code Playgroud)
这种新类型意味着:
g :: forall a. Action IO a -> IO a
g(即actionFile)的来电者必须选择ag(即actionFile)的来电者必须提供Action IO ag必须提供一个IO aStringactionFile必须回答IO ()这使得可以根据需要actionFile进行选择a.