如何在Haskell中编写常见的“if”分支

时间:2010-11-28 13:09:14

标签: haskell

我有以下代码片段:

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'?我不认为有任何副作用,是吗?

4 个答案:

答案 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

是的,我知道,帮手功能。对不起,这就是我在现实生活中实际写的。 :)