代码之家  ›  专栏  ›  技术社区  ›  ondra

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

  •  5
  • ondra  · 技术社区  · 15 年前

    我有以下代码片段:

    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)
    

    如何用“nice Haskell”编写这段代码?我在想梅贝特蒙纳德,但不知怎么搞不好。我试着做一些“提升”,但没能把这些类型粘在一起。我可以更改getIfaceAddr/getDestAddr的签名。

    另请注意:inet为什么是“HostAddress->IO String”?我认为没有任何副作用,是吗?

    4 回复  |  直到 15 年前
        1
  •  5
  •   luqui    15 年前

    天哪,那是什么 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
  •  6
  •   sclv    15 年前

    另一个没有帮助的解决方案:

    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。

    编辑:这里有一个指向可遍历的文档的链接,这是一个被忽略但又常常是不可或缺的类型类: http://haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/Data-Traversable.html

        3
  •  1
  •   luqui    15 年前

    一个helper函数可以通过模式匹配做到这一点?

    help x y
         where
         help (Just a) (Just b) = -- actions here ?
         help _        _        = return ()
    
        4
  •  1
  •   Yitz    15 年前

    您可以将其写为“如果分支”,如下所示:

    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 类型已经意味着“可能失败的事情”,并且有一些内置函数允许我们在使用 也许 吧 价值观。

    所以我可能会这样写:

    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
    

    是的,我知道,一个助手函数。抱歉,这就是我在现实生活中的写法。:)

    推荐文章