2010-11-28 35 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 

是啊,我知道,一個輔助功能。對不起,這就是我現實生活中的寫作方式。 :)