2010-11-28 70 views
5

“如果”分支我有下面的代码片段:如何写常见于哈斯克尔

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) 

如何写“好哈斯克尔”这个代码?我正在考虑MaybeT monad,但不知何故无法实现。我试图做一些“提升”,但无法将这些类型拼凑在一起。我可以更改getIfaceAddr/getDestAddr的签名。

作为旁注:为什么inet_ntoa'HostAddress - > IO String'?我不认为有任何副作用,是吗?

回答

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,然后在将它绑定到main之前将其恢复为IO

1

辅助函数可以通过模式匹配来做到这一点吗?

help x y 
    where 
    help (Just a) (Just b) = -- actions here ? 
    help _  _  = return() 
+0

是的 - 我可以将getIfaceAddr的结果传递给辅助函数,并在那里执行inet_ntoa。对于所有内容,我只是对“创建帮助函数”的答案感到有些厌倦。 – ondra 2010-11-28 13:33:58

+0

幸运的是,您可以使用`liftM2`函数来控制``Maybe` monad`从Control.Monad`,而不是自己滚动。 – nponeccop 2011-11-29 16:31:35

6

另外,helperless解决方案:

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

你可以把它写为“如果分支”是这样的:

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 

是啊,我知道,一个辅助功能。对不起,这就是我现实生活中的写作方式。 :)