Haskell中do块内的where子句语法

28

我正在尝试重构Haskell中do块内的一个mapM_函数调用。我想将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吗?

3个回答

39

这里有三种类似但又不同的定义方式:

  • 你可以在某些定义之后添加where从句--大多数是类似等式的绑定。因此,你可以在函数结尾、或者在使用了let或周围的where从句定义的东西之后加一个where从句。

  • 另一方面,let x = ... in ...是一个表达式,它求值为in之后的部分,该部分是let之后的内容唯一可见的地方。

  • do块内,因为已经有了一个隐含的作用域嵌套(在定义后的位置才会可见),所以你只需要使用单独的let x = ...。这实际上与前一种形式是相同的--let之后的do块的其余部分就是in ...部分。

如果你想要一个使用在do块内已经定义过的东西的局部定义,你唯一的选择就是第三个(或将其他值作为参数传递)。然而,对于像你的示例一样的独立辅助函数,任何形式都可以。这里是你的示例,以演示每个形式:

第一种形式,其中funcfoo中任何地方都可见,包括在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块内部定义。这种情况下,在let之后才能看到func;在第一个...中它还没有被定义。

<code>foo = do ...
         let func x = x + 1
         mapM_ func aList
         ...
         return aValue
</code>

另外,为了保证准确性:由于 let ... in ... 是一个表达式,你也可以在任何需要表达式的地方使用它,来命名一些本地定义。下面是另一个例子:

<code>foo = do ...
         let func x = x + 1 in mapM_ func aList
         ...
         return aValue
</code>

与之前一样,func仅在let表达式内可见,在此情况下它是其后的单个表达式中可见,而其他地方则不可见。


谢谢。第三种形式看起来可以让我定义 lambda 函数接近 mapM_,非常有用。我只担心它会在顶层函数命名空间中污染 let 中定义的 func 名称(小问题)。 - Ralph
@Ralph:这只是关于你想让它在哪个范围内可见的问题。如果你的“do”块足够大,需要担心其中的命名空间污染,那么你应该考虑将其分成更小的部分。:] - C. A. McCann
是的,我也想到了。我正在翻译一些功能Scala代码,这些代码是某个白痴写的(:-)),其中的函数比它们应该的要长。 - Ralph

10

另一个选择是使用forM_代替mapM_,这会颠倒参数顺序。然后,您可以使用带有尾随 lambda 表达式的 $ 运算符,像这样:

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

  return aValue

我没有考虑过那个替代方案。我会再次查看我的代码,看看是否更易读。在其他需要单子列表遍历的情况下,但是没有像 forM_ 这样方便的翻转版本时,我可以使用 flip 来创建一个新函数。 - Ralph

3

你的where应该放在函数末尾,像这样:

如下:

function aList aValue = do
    mapM_ func aList
    return aValue
    where func x = x + 1

我不确定。我想找到一种方法将lambda提取到一个非常本地的函数中,以提高可读性。如果我必须将它放在顶层函数的末尾,那么它的可用性就会大大降低。我是Haskell的新手。 - Ralph

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接