我可以避免在Haskell中"向右漂移"吗?

Owe*_*wen 11 haskell coding-style

当我使用命令式语言时,我经常编写代码

foo (x) {
    if (x < 0) return True;
    y = getForX(x);
    if (y < 0) return True;

    return x < y;
}
Run Code Online (Sandbox Code Playgroud)

也就是说,我逐个检查条件,尽快打破障碍.

我喜欢这个,因为它使代码保持"平坦"并遵循"最终重量"的原则.我认为它更具可读性.

但在哈斯克尔,我会把它写成

foo x = do
    if x < 0
        then return x
        else do
            y <- getForX x

            if y < 0
                then return True
                else return $ x < y
Run Code Online (Sandbox Code Playgroud)

我不喜欢哪个.我可以使用一个允许爆发的monad,但是因为我已经使用了monad,所以我必须使用lift所有内容,如果可以的话,我会想要避免添加单词.

我想这并不是一个完美的解决方案,但有人有任何建议吗?

fuz*_*fuz 13

针对您的具体问题:悬空do符号和逻辑用法如何?

foo x = do
  if x < 0 then return x else do
  y <- getForX x
  return $ y < 0 || x < y
Run Code Online (Sandbox Code Playgroud)

编辑

结合hammar所说,你甚至可以得到更漂亮的代码:

foo x | x < 0     = return x
      | otherwise = do y <- getForX x
                       return $ y < 0 || x < y
Run Code Online (Sandbox Code Playgroud)

  • 这些都是很好的答案,但我认为"晃来晃去"会对我有所帮助. (2认同)

ham*_*mar 12

使用模式和警卫可以提供很多帮助:

foo x | x < 0 = return x
foo x = do
    y <- getForX x
    if y < 0
        then return True
        else return $ x < y
Run Code Online (Sandbox Code Playgroud)

您还可以在where子句中引入小辅助函数.这往往有助于提高可读性.

foo x | x < 0 = return x
foo x = do
    y <- getForX x
    return $ bar y
  where
    bar y | y < 0     = True
          | otherwise = x < y
Run Code Online (Sandbox Code Playgroud)

(或者,如果代码真的像这个例子一样简单,请使用FUZxxl建议的逻辑).


Has*_*ant 8

执行此操作的最佳方法是使用警卫,但是您需要先获取y值才能在警卫中使用它.这需要从getForX可能隐藏到某些monad中获得,除非通过getForX(例如IOmonad),否则你无法获得价值,然后你必须解除使用守卫进入该monad的纯函数.一种方法是使用liftM.

foo x = liftM go (getForX x)
  where
    go y | x < 0     = True
         | y < 0     = True
         | otherwise = x < y
Run Code Online (Sandbox Code Playgroud)