haskell - 奇怪的单子行为
问题描述
我试图编写一个程序,它将文件路径作为(命令行)参数并返回每个文件的第一行
main = getArgs >>= ( mapM_ ( \file -> ( openFile file ReadMode >>= ( (\handle -> hGetLine handle >>= print) >> hClose ) ) ) )
我知道这看起来不太漂亮,但我只是 Haskell 的初学者。我也故意避免使用 do 符号,因为我对她(还)感觉不太舒服。
因此,上面的代码编译并为无效的文件路径返回错误,而对于有效路径则没有任何内容(即尤其不是文件的第一行)。
我必须承认我几乎不知道我做错了什么,但我做了以下观察:
如果我添加以下内容来检查哪些部分仍然被执行
main = getArgs >>= ( mapM_ ( \file -> ( openFile file ReadMode >>= ( (\handle -> hGetLine handle >>= print) >> (const $ putStr "Hello1") >> hClose >> (const $ putStr "Hello2") ) ) ) )
程序只打印第二个“Hello”,这让我想起了 (>>) 的类型签名:
(>>) :: Monad m => m a -> m b -> m b
所以考虑到只有第二个参数类型的东西被返回,也许第一个参数被忽略了?
但是反对这个理论的第一个论点是这样的函数似乎不是很有用(至少在 IO Monad 的上下文中不是),第二个是程序
main = (putStr "Hello" >> putStr "World" >> putStr "!")
返回“你好世界!” 正如预期的那样。因此,我一定完全走错了路,这就是我来这里的原因。
谢谢你的帮助!
解决方案
我认为您的主要错误是您弄乱了句柄:
main = getArgs >>= (mapM_ (\file -> (openFile file ReadMode >>= (\handle -> (hGetLine handle >>= print) >> hClose handle) ) ) )
你这样做>>
是为了 ( -> handle
) Monad(它是一个 reader monad - 看到有一个for constant的Monad
实例)而不是!(->) c
c
IO
所以它确实将句柄传递给两者hGetLine handle >>= print
,hClose
但>>
忽略了第一个结果 IO 操作并将hClose
结果返回给>>
这里的效果是通过句柄!
所以是的,最后唯一执行的 IO 效果是关闭文件!
这是微妙的,并不明显,因为你很少看到/想到这样的 reader-monad 实例。
这是带有do
符号的
main = do
args <- getArgs
mapM_ (\file -> do
handle <- openFile file ReadMode
line <- hGetLine handle
print line
hClose handle) args
我建议将 args 参数切换到forM_
(from Control.Monad
):
main = do
args <- getArgs
forM_ args (\file -> do
handle <- openFile file ReadMode
line <- hGetLine handle
print line
hClose handle)
现在您应该确保关闭手柄 - 您可以使用bracket
from Control.Exception
:
main = do
args <- getArgs
forM_ args (\file -> do
bracket
(openFile file ReadMode)
hClose
(\h -> do
line <- hGetLine h
print line
)
)
或(因为这很常见)只是withFile
为System.IO
您打开/关闭:
main = do
args <- getArgs
forM_ args (\file -> do
withFile file ReadMode
(\h -> do
line <- hGetLine h
print line
)
)
最后,您实际上不必使用所有 可以使用 ( lazy )的句柄东西,并且对于空文件也更安全一些:readFile
main = do
args <- getArgs
forM_ args (\file -> do
content <- readFile file
let ls = lines content
case ls of
[] -> putStrLn "no line in file"
(firstLine:_) -> putStrLn firstLine
)
推荐阅读
- java - 需要最佳实体和查询设计来获取具有多个关联的对象
- python - 尝试在 Postgre DB 上迁移模型时出现 Django 错误
- sql-server - node/mssql/async 尝试连续执行未知数量的查询
- ruby-on-rails - Rails ActiveRecord 将不相关的表包含到查询中
- php - I want to sum two columns' row and then the value insert the other column's row in the same table using php update. Is it possible?
- android - 使用 Xamarin.Forms 为 Android 版本构建时,“LinkAssemblies”任务意外失败
- python - 跟踪计算哪个子集以避免混合结果
- stripe-payments - 如何使用条带添加资金不足的支付来源?
- r - 用 R Markdown 编织的 ggplot barplot
- mediawiki - 相同的页面名称。命名空间?别名?等级制度?