2010-11-28 4 views
5

У меня есть следующий фрагмент кода:Как написать общий «если» ветвление в 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, но почему-то не смог заставить ее работать. Я пытался сделать некоторые «подъемы», но не смог сшить типы вместе. Я могу изменить подпись 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, чтобы вернуть его в IO, прежде чем привязывать его к main.

1

Помощник может сделать это с рисунком сопоставив?

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

Yep - Я могу передать результат getIfaceAddr на вспомогательную функцию и сделать там inet_ntoa. Я просто немного устал от ответа «Создать помощник» на все. – ondra 2010-11-28 13:33:58

+0

К счастью, вы можете использовать функцию `liftM2` для монашки` Maybe` из `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, просто сопоставив паттерн прямо из пары.

Edit: Вот ссылка на документацию для проходимой, с упускается, но часто незаменимой класс типов: 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 

Да, я знаю, вспомогательную функцию. Извините, вот как я на самом деле написал это в реальной жизни. :)

Смежные вопросы