25

我正在尝试在 HaskellmapM_的块内重构函数调用。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代替吗?

4

3 回答 3

35

这里有三种相似(但不同)的定义方式:

  • 您可以在某些定义之后附加where子句——主要是方程式样式的绑定。因此,您可以将一个放在函数的末尾,或者在使用let或周围where子句定义的内容之后。

  • 另一方面,let x = ... in ...是一个表达式,其计算结果为 after 部分in,这是后面的东西唯一let可见的地方。

  • 在一个do块内,因为已经有一个隐式嵌套的范围(事物在它们第一次定义后可见),你可以let x = ...单独使用。这实际上与之前的形式相同——do之后的块的其余部分let实际上是in ...部分。

如果您想要一个使用do块中定义的内容的本地定义,您唯一的选择是第三个(或将其他值作为参数传递)。但是,对于像您的示例这样的独立辅助函数,任何样式都可以使用。这是您的示例,以演示每个示例:

第一种样式, where 在func中的任何地方可见,包括子句foo中定义的任何其他内容:where

foo = do ...
         mapM_ func aList
         ...
         return aValue
  where func x = x + 1

第二种样式, wherefunc仅在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表达式内部可见,在这种情况下,它是它之后的单个表达式,在其他任何地方都看不到。

于 2012-12-30T17:44:59.730 回答
10

另一种选择是使用forM_而不是mapM_,它会翻转参数的顺序。然后,您可以使用$带有尾随 lambda 表达式的运算符,如下所示:

do
  forM_ aList $ \x -> do
    ...

  return aValue
于 2012-12-30T17:50:02.713 回答
3

你不where应该在函数的末尾吗?

像这样:

function aList aValue = do
    mapM_ func aList
    return aValue
    where func x = x + 1
于 2012-12-30T17:33:08.130 回答