【发布时间】:2015-11-08 10:34:51
【问题描述】:
我想知道在 IO Monad 中是否有一种惯用的方式来编写类似于链式 if/else 语句的控制代码。
所以在像 Python 这样的语言中,我通常会这样写:
if οs.path.isdir(fname):
# do whatever
elif os.path.isfile(fname):
# ...
else:
# ...
我在 Haskell 中能想到的最好的方法如下:
isf <- doesFileExist path
isd <- if isf then return False else doesDirectoryExist path
case (isf, isd) of
(True, _) -> return ...
(_, True) -> return ...
_ -> return ...
这不是那么好,我想知道是否有更好的方法来写这种东西。
另外,为了验证我的理解:如果您不想总是同时执行这两个操作,则在 IO Monad 的情况下,isd <- ... 中的 if isf 部分是必需的。我的猜测是,在其他 Monad(惰性 Monad?)中,这将是不需要的,因为 isd 会被惰性评估。
编辑
基于第一个 cmets,我最终得到以下结果:
firstMatchM :: (Monad m) => a -> [(a -> m Bool, b)] -> b -> m b
firstMatchM arg [] def = return def
firstMatchM arg ((check,x):xs) def = do
t <- check arg
if t then return x else firstMatchM arg xs def
doFirstM :: (Monad m) => a -> [(a -> m Bool, a -> m b)] -> (a -> m b) -> m b
doFirstM arg acts def = do
fm <- firstMatchM arg acts def
fm arg
handlePath2 path = doFirstM path
[( \p -> doesFileExist p,
\p -> return "file"
),(\p -> doesDirectoryExist p,
\p -> return "dir"
)] $ \p -> return "Error"
和@chi 的第二个建议类似,我更喜欢ifM,因为它更接近命令式版本。
【问题讨论】:
-
但即使在 python 中你也应该重构那些 ;)
-
好的,我会咬的,怎么样? :)
-
好吧,你可以做一些事情,比如建立一个动作列表,并使用
foldM或forM等东西来获得你想要的结果。这将推广到任意数量的elifs,尽管仅对 3 个案例会很麻烦。在 python 中也是如此:for test, action in tests_and_actions: if test(input): action(input). -
@ynimous 例如分成 3 个函数/动作,每个
guarding ;) 然后你可以对它们进行排序 ;) (当然when也可以) -
你可以制作
ifM版本slightly nicer。