我有以下代码片段:
srcaddr <- getIfaceAddr iface >>= inet_ntoa . fromJust
dstaddr <- getDestAddr iface >>= inet_ntoa . fromJust
-- I want to perform actions only if neither getIfaceAddr
-- nor getDestAddr returned Nothing
action1 srcaddr dstaddr
action2 srcaddr dstaddr
action3 srcaddr dstaddr
getIfaceAddr :: String -> IO (Maybe HostAddress)
getDestAddr :: String -> IO (Maybe HostAddress)
如何在'漂亮的Haskell'中编写此代码?我在考虑MaybeT monad,但不知何故无法使它工作。我试图做一些“提升”,但无法将这些类型组合在一起。我可以更改getIfaceAddr / getDestAddr的签名。
作为旁注:为什么是inet_ntoa'HostAddress-&gt; IO String'?我不认为有任何副作用,是吗?
答案 0 :(得分:6)
另一个无助的解决方案:
msrcaddr <- getIfaceAddr iface >>= traverse inet_ntoa
mdstaddr <- getDestAddr iface >>= traverse inet_ntoa
case liftM2 (,) msrcaddr mdstaddr of
Just (srcaddr,dstaddr) ->
action1 srcaddr dstaddr
action2 srcaddr dstaddr
action3 srcaddr dstaddr
Nothing -> return ()
如果您愿意,也可以使用maybe
替换案例。或者你可以直接通过模式匹配来避免liftM2。
编辑:这是Traversable文档的链接,这是一个被忽视但经常不可或缺的类型类:http://haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/Data-Traversable.html
答案 1 :(得分:5)
哦,我的,fromJust
是什么?如果getIfaceAddr
返回Nothing
,则此代码会使您的程序崩溃。
MaybeT
解决方案如下所示:
srcaddr <- lift . inet_ntoa =<< MaybeT (getIfaceAddr iface)
dstaddr <- lift . inet_ntoa =<< MaybeT (getDestAddr iface)
lift $ do
action1 srcaddr dstaddr
...
第一行的类型如下所示:
getIfaceAddr iface :: IO (Maybe HostAddress)
MaybeT (getIfaceAddr iface) :: MaybeT IO HostAddress
inet_ntoa :: HostAddress -> IO String
lift . inet_ntoa :: HostAddress -> MaybeT IO String
lift . inet_ntoa =<< MaybeT (getIfaceAddr iface)
:: MaybeT IO String
请注意,您的代码将具有MaybeT IO something
类型,因此您需要runMaybeT
将其重新绑定到IO
,然后再将其绑定到main
。
答案 2 :(得分:1)
辅助函数可以通过模式匹配来实现吗?
help x y
where
help (Just a) (Just b) = -- actions here ?
help _ _ = return ()
答案 3 :(得分:1)
您可以将其写为“if-branching”,如下所示:
import Control.Monad (when)
import Data.Maybe (isJust)
...
mSrcaddr <- fmap inet_ntoa $ getIfaceAddr iface
mDstaddr <- fmap inet_ntoa $ getDestAddr iface
when (isJust mSrcaddr && isJust mDstaddr) $ do
let Just srcaddr = mSrcaddr
Just dstaddr = mDstaddr
action1 srcaddr dstaddr
action2 srcaddr dstaddr
action3 srcaddr dstaddr
但是我不喜欢养成那些可能会失败并导致程序崩溃的模式匹配的坏习惯,即使在这种情况下它是安全的。
另外,我不喜欢使用isJust
和朋友并手动测试; Maybe
类型已经意味着“可能失败的东西”,并且内置函数允许我们在使用Maybe
值时保留该含义。
所以我可能会这样写:
import Control.Applicative (liftA2)
import Data.Maybe (fromMaybe)
...
mSrcaddr <- fmap inet_ntoa $ getIfaceAddr iface
mDstaddr <- fmap inet_ntoa $ getDestAddr iface
fromMaybe (return ()) $ liftA2 doActions mSrcaddr mDstaddr
where
doActions srcaddr dstaddr = do
action1 srcaddr dstaddr
action2 srcaddr dstaddr
action3 srcaddr dstaddr
是的,我知道,帮手功能。对不起,这就是我在现实生活中实际写的。 :)