`do`和`where`如何混合?

时间:2017-07-25 14:50:04

标签: haskell

从某本书中我有以下代码片段

mutableUpdateIO :: Int -> IO (MV.MVector RealWorld Int)
mutableUpdateIO n = do
  mvec <- GM.new (n + 1)
  go n mvec
  where
    go 0 v = return v
    go n v = (MV.write v n 0) >> go (n - 1) v

mutableUpdateST :: Int -> V.Vector Int
mutableUpdateST n =
  runST $ do
    mvec <- GM.new (n + 1)
    go n mvec
  where
    go 0 v = V.freeze v
    go n v = (MV.write v n 0) >> go (n - 1) v
hindent一样缩进它们。现在我想介绍所有大括号和分号,因此空格不再相关。只是因为我很好奇。

第二个示例表明,where属于整个runST $ do ...表达式,但第一个示例表明,where在某种程度上是go n mvec语句的一部分。阅读Haskell Report Chapter 2.7我尝试在第一个例子中引入大括号和分号,如

mutableUpdateIO :: Int -> IO (MV.MVector RealWorld Int)
mutableUpdateIO n = do {
  mvec <- GM.new (n + 1);
  go n mvec;
  where {
    go 0 v = return v;
    go n v = (MV.write v n 0) >> go (n - 1) v;
  } ; }

但是我得到了一个解析错误。那是为什么?

为什么布局hindent会为第一个示例mutableUpdateIO生成有效的Haskell?不应该像我上面的尝试那样引入括号和分号吗?

1 个答案:

答案 0 :(得分:9)

where块既不属于runST $ do ...表达式,也不属于go n mvec语句;它们属于mutableUpdateIO n = ...声明和mutableUpdateST n = ...声明。大括号和分号应该是这样的:

mutableUpdateIO :: Int -> IO (MV.MVector RealWorld Int)
mutableUpdateIO n = do {
  mvec <- GM.new (n + 1);
  go n mvec;
  } where {
    go 0 v = return v;
    go n v = (MV.write v n 0) >> go (n - 1) v;
  }

第2.7章报告中非正式描述的相关句子是:

  

每当包含布局列表的句法类别结束时,也会插入一个闭括号;也就是说,如果在近距离支撑合法的位置遇到非法词汇,则会插入一个紧支撑。

由于where是表达式中的非法词汇,因此结束do块并在那里插入一个紧括号。这也解释了为什么所产生的布局是合法的。