10

我有一个非常简单的问题。我想在使用绑定运算符的代码块之后使用 where 子句,但出现编译错误。

这是一个简单的例子:

main =
    putStrLn "where clause test:" >>
    return [1..10] >>= \list ->
    print list'
        where list' = reverse list -- test1.hs:5:28: Not in scope: `list'

我可以对 list' 使用 let 子句,如

main =
    putStrLn "where clause test:" >>
    return [1..10] >>= \list ->
    let list' = reverse list -- works of course
    in print list'

但如果我可以使用 where 子句,我真的很喜欢它......

我也尝试过使用 do 表示法

main = do
    putStrLn "where clause test:"
    list <- return [1..10]
    print list'
        where list' = reverse list --test3.hs:5:30: Not in scope: `list'

同样的问题。在这些情况下我可以使用 where 子句吗?

4

3 回答 3

11

问题是let-in是一个表达式,可以在其他表达式中使用,而where只能用于 (module|class|instance|GADT|...) 声明或 (function|pattern) 绑定。

从 Haskell 98 关于声明和绑定的报告中,

p | g 1 = e 1
    | g 2 = e 2
    ...
    | g m = e m
  where { 声明 }

是糖

p = let decls in
      if g 1 then e 1 else
      if g 2 then e 2 else
      …
      if g m then e m else error "Unmatched pattern"

或者,通过移除守卫来简化事情,

p = e where { 声明 }

是糖

p = let 下降 in _

在函数和模式绑定中。即使你的e是一个do {……}结构也是如此。

如果您想在较大的表达式中对特定子表达式进行本地绑定,则需要使用let- in(或者只是let在 a 内do,但这只是let-的糖in)。

你甚至不能写

main = do
    putStrLn "where clause test: "
    list <- return [1..10]
    (print list' where list' = reverse list)

因为“ e where { decls } ”不是一个合法的表达式——where只能用在声明和绑定中。

main = do
    putStrLn "where clause test: "
    list <- return [1..10]
    let list' = list'' where list'' = reverse list
    print list'

这是合法的(如果有点做作)。

于 2009-07-21T04:15:49.810 回答
11

正如 ehemient 所解释的,你不能像现在这样使用where从句。

发生错误是因为在此代码中:

main =
  return [1..10] >>= \list ->
  print list'
    where
      list' = reverse list

-where子句附加到 main 函数。

这是带有更多括号的相同函数:

main = return [1..10] >>= (\list -> print list')
  where
    list' = reverse list

我认为你得到“ out of scope”错误的原因很明显:绑定listmain表达式内部,而不是where子句可以达到的东西。

在这种情况下我通常会做什么(我已经被同样的事情咬过很多次了)。我只是简单地介绍一个函数并将其list作为参数传递。

main = do
  list <- return [1..10]
  let list' = f list
  print list'
  where
    f list = reverse list -- Consider renaming list,
                          -- or writing in point-free style

当然,我想你在f函数中的实际代码远不止于此reverse,这就是为什么你希望它在where子句中,而不是内联let绑定。如果函数内部的代码f非常小,我就直接写在let绑定里面,就不会经历引入新函数的开销。

于 2009-07-21T06:02:46.973 回答
1

据我所知, where 子句仅用于本地绑定。>>(=) 绑定语句的内部不是本地绑定(该语句中有两种不同的绑定)。

与此比较:

main = f [1..10]

f list =
    putStrLn "where clause test:" >> print list'
        where list' = reverse list

您可能想参考Haskell 98 语法报告——不确定它会有多大帮助。

如果我错了,肯定会有人纠正我,但我很确定你不能在上面显示的样式中使用 where 子句。 list除非它是函数的参数,否则永远不会在 where 子句的范围内。

于 2009-07-21T03:26:55.417 回答