在haskell tutorial之后,作者提供了 withFile 方法的以下实现:
withFile' :: FilePath -> IOMode -> (Handle -> IO a) -> IO a
withFile' path mode f = do
handle <- openFile path mode
result <- f handle
hClose handle
return result
但为什么我们需要将result
包裹在return
中?提供的函数f
是否已经返回IO
,因为它的类型Handle -> IO a
可以看到它?
答案 0 :(得分:7)
你是对的:f
已经返回IO
,所以如果函数是这样写的:
withFile' path mode f = do
handle <- openFile path mode
f handle
没有必要返回。问题是hClose handle
介于两者之间,所以我们必须先存储结果:
result <- f handle
并且<-
正在摆脱IO
。所以return
会把它放回去。
答案 1 :(得分:3)
这是我第一次尝试使用Haskell时困扰我的一件小事。你在误写中误解了<-
结构的含义。 result <- f handle
并不代表&#34;将f handle
的值分配给result
&#34 ;;它意味着&#34;将result
绑定到一个值&#39;提取&#39;来自f handle
&#34;的monadic值(&#39;提取&#39;以某种方式发生,由您正在使用的特定Monad实例定义,在本例中为IO monad)。
即,对于某些Monad类型类m,<-
语句在右侧使用类型m a
的表达式,在左侧使用类型a
的变量,将变量绑定到一个值。因此,在您的特定示例中,使用result <- f handle
,我们有f result :: IO a
,result :: a
和return result :: IO a
类型。
PS do-notation还有let
的特殊形式(在这种情况下没有in
关键字!),作为<-
的纯对应物。所以你可以将你的例子重写为:
withFile' :: FilePath -> IOMode -> (Handle -> IO a) -> IO a
withFile' path mode f = do
handle <- openFile path mode
let result = f handle
hClose handle
result
在这种情况下,由于let
是一项简单的分配,因此result
的类型为IO a
。