我正在尝试重构Haskell中mapM_
块内的do
函数调用。我想将lambda提取到(本地)命名函数,以使代码更具可读性。
我的代码最初看起来像这样:
do
-- ...
mapM_ (\x -> x + 1) aList
return aValue
我想将其更改为
do
-- ...
mapM_ func aList
where func x = x + 1
return aValue
但我在return aValue
行收到语法错误。我的实际lambda更复杂:-),但我确实尝试使用相同的lambda来确保它不是lambda代码中的问题。
如何重写此代码?我应该使用let
... in
吗?
答案 0 :(得分:31)
这里有三种相似(但不同)的定义方法:
您可以在某些定义之后附加where
子句 - 主要是方程式绑定。因此,您可以在函数末尾添加一个,或者在使用let
或周围where
子句定义的内容之后添加一个。
另一方面,let x = ... in ...
是一个表达式,它评估in
之后的部分,这是let
之后的唯一地方是可见的。
在do
块内部,因为已经有隐式嵌套范围(事物在首次定义后可见),所以您可以单独使用let x = ...
。这与前一个表单完全相同 - do
之后的let
块的其余部分实际上是in ...
部分。
如果您想要使用do
块中定义的内容的本地定义,您唯一的选择是第三个(或将其他值作为参数传递)。但是,对于像您的示例一样的独立辅助函数,任何样式都可以。以下是您的示例,以演示每个:
第一种样式,其中func
在foo
中的任何位置都可见,包括where
子句中定义的任何其他内容:
foo = do ...
mapM_ func aList
...
return aValue
where func x = x + 1
第二种样式,其中func
仅在let
表达式中可见,在这种情况下是整个do
块:
foo = let func x = x + 1
in do
...
mapM_ func aList
...
return aValue
第三种风格,在do
区块内定义它。在这种情况下,func
仅在let
之后可见;在第一个...
中,它还没有定义。
foo = do ...
let func x = x + 1
mapM_ func aList
...
return aValue
哦,好的方法:由于let ... in ...
是一个表达式,你也可以在任何有表达式的地方使用它来命名一些本地定义。所以这是另一个例子:
foo = do ...
let func x = x + 1 in mapM_ func aList
...
return aValue
和以前一样,func
仅在let
表达式中可见,在这种情况下,它是单个表达式,在其他地方之后。
答案 1 :(得分:10)
另一种选择是使用forM_
而不是mapM_
,它会翻转参数的顺序。然后,您可以使用$
运算符和尾随的lambda表达式,如下所示:
do
forM_ aList $ \x -> do
...
return aValue
答案 2 :(得分:3)
你的where
不应该在功能的最后?
像这样:
function aList aValue = do
mapM_ func aList
return aValue
where func x = x + 1