当我使用命令式语言时,我经常编写像
这样的代码foo (x) {
if (x < 0) return True;
y = getForX(x);
if (y < 0) return True;
return x < y;
}
也就是说,我一个接一个地检查条件,尽快突破区块 尽可能。
我喜欢这个,因为它使代码“平淡”并遵循“结束”的原则 重量“。我认为它更具可读性。
但是在Haskell中我会把它写成
foo x = do
if x < 0
then return x
else do
y <- getForX x
if y < 0
then return True
else return $ x < y
我不喜欢哪个。我可以使用允许爆发的monad,但是
因为我已经使用了monad,所以我必须lift
一切,这会增加单词
如果可以,我想避免。
我认为这并不是一个完美的解决方案,但有人有 有什么建议吗?
答案 0 :(得分:13)
针对您的具体问题:悬挂do
符号和逻辑用法怎么样?
foo x = do
if x < 0 then return x else do
y <- getForX x
return $ y < 0 || x < y
结合hammar的说法,你甚至可以得到更漂亮的代码:
foo x | x < 0 = return x
| otherwise = do y <- getForX x
return $ y < 0 || x < y
答案 1 :(得分:12)
使用模式和警卫可以提供很多帮助:
foo x | x < 0 = return x
foo x = do
y <- getForX x
if y < 0
then return True
else return $ x < y
您还可以在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
(或者如果代码真的像这个例子一样简单,请使用FUZxxl建议的逻辑)。
答案 2 :(得分:7)
执行此操作的最佳方法是使用警卫,但是您需要先使用y
值才能在警卫中使用它。这需要从getForX
获得,可能隐藏在某些monad中,除非通过getForX(例如IO
monad),否则你无法获得价值,然后你必须提升纯函数那个使用守卫进入那个monad。一种方法是使用liftM
。
foo x = liftM go (getForX x)
where
go y | x < 0 = True
| y < 0 = True
| otherwise = x < y
答案 3 :(得分:3)
不仅仅是
foo x = x < y || y < 0 where y = getForX x
编辑:正如Owen指出的那样 - getForX是monadic所以我上面的代码不起作用。以下版本可能应该:
foo x = do
y <- getForX x
return (x < y || y < 0)