2010-11-28 8 views
5

Je l'extrait de code de code suivant:Comment écrire commun « si » ramification dans 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) 

Comment écrire ce code dans « nice Haskell »? Je pensais à la monade MaybeT, mais d'une manière ou d'une autre, je n'arrivais pas à la faire fonctionner. J'essayais de faire un peu de «levage», mais je n'arrivais pas à assembler les types ensemble. Je peux changer la signature de getIfaceAddr/getDestAddr.

En tant que sidenote: pourquoi inet_ntoa 'HostAddress -> IO String'? Je ne pense pas qu'il y ait d'effets secondaires, n'est-ce pas?

Répondre

5

Oh mon Dieu, qu'est-ce que c'est fromJust? Si getIfaceAddr renvoie Nothing, ce code plantera votre programme.

La solution MaybeT ressemble à ceci:

srcaddr <- lift . inet_ntoa =<< MaybeT (getIfaceAddr iface) 
dstaddr <- lift . inet_ntoa =<< MaybeT (getDestAddr iface) 
lift $ do 
    action1 srcaddr dstaddr 
    ... 

Les types pour la première ligne se emboîtent comme ceci:

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 

Rappelez-vous que votre code va avoir le type MaybeT IO something, de sorte que vous avez à runMaybeT pour le récupérer en IO avant de le lier à main.

+0

Oui, c'est pourquoi je demande - parce que je n'ai pas aimé le juste :) – ondra

1

Une fonction d'aide peut faire cela avec la correspondance de modèle?

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

Oui - Je pourrais passer le résultat de getIfaceAddr à la fonction d'assistance et y faire l'inet_ntoa. Je suis juste un peu fatigué de la réponse 'Créer une fonction d'aide' sur tout. – ondra

+0

Heureusement, vous pouvez utiliser la fonction 'liftM2' pour la monade' Maybe' de 'Control.Monad' au lieu de lancer la vôtre. – nponeccop

6

Une autre solution 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() 

Vous pouvez également remplacer le cas avec un maybe, si vous préférez. Ou vous pouvez éviter le liftM2 par juste un motif correspondant directement à la paire.

Edit: Voici un lien vers la documentation Traversable, un négligé mais souvent indispensable classe de types: http://haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/Data-Traversable.html

1

Vous pouvez l'écrire comme un « si ramification » comme ceci:

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 

Mais je Je n'aime pas avoir la mauvaise habitude d'écrire les types de correspondances qui pourraient potentiellement échouer et planter mon programme, même si dans ce cas, il est sûr.

En outre, je n'aime pas utiliser isJust et amis et de tester manuellement; le type Maybe signifie déjà "quelque chose qui pourrait échouer", et il y a des fonctions intégrées qui nous permettent de conserver cette signification tout en utilisant les valeurs Maybe.

donc je ne serais probablement écrire comme ceci:

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 

Ouais, je sais, une fonction d'aide. Désolé, c'est comme ça que je l'écrirais dans la vraie vie. :)