4

wheredo块内的语义上苦苦挣扎,特别是Test.Hspec. 以下作品:

module ExampleSpec where

import Test.Hspec
import Test.QuickCheck

spec :: Spec
spec = do
    describe "foo" $ do
        let
            f = id
            in
                it "id" $ property $
                    \x -> f x `shouldBe` (x :: Int)
    describe "bar" $ do
        it "id" $ property $
            \x -> x `shouldBe` (x :: Int)

这不会:

module ExampleSpec where

import Test.Hspec
import Test.QuickCheck

spec :: Spec
spec = do
    describe "foo" $ do
        it "id" $ property $
            \x -> f x `shouldBe` (x :: Int)
        where
            f = id
    describe "bar" $ do
        it "id" $ property $
            \x -> x `shouldBe` (x :: Int)

它失败了:

/mnt/c/haskell/chapter15/tests/ExampleSpec.hs:13:5: error: parse error on input ‘describe’
   |
13 |     describe "bar" $ do
   |     ^^^^^^^^

我做错了什么还是这是某种固有的限制where

4

2 回答 2

3

where子句只能附加到函数或大小写绑定,并且必须位于右侧主体之后。

当编译器看到where时,它就知道spec = ...等式的 RHS 结束了。然后它使用缩进来确定扩展内的定义块有多远where(在这种情况下只是单个f = id)。之后编译器正在寻找下一个模块范围定义的开始,但是缩进describe "bar" $ do对于定义的开始无效,这是您得到的错误。

您不能将where子句随机插入函数定义的中间。它只能用于在绑定的整个 RHS 范围内添加辅助绑定;它不能用于在任意子表达式的范围内添加本地绑定。

然而,let ... in ...正是为了这个目的。并且由于您do在 each 下使用块describe,您还可以使用let语句(使用块的其余部分do来界定本地绑定的范围,而不是表达式的in一部分)。let ... in ...所以你可以这样做:

spec = do
    describe "foo" $ do
        let f = id
        it "id" $ property $
            \x -> f x `shouldBe` (x :: Int)
    describe "bar" $ do
        it "id" $ property $
            \x -> x `shouldBe` (x :: Int)
于 2021-11-30T22:01:55.343 回答
2

这是为 where 块的范围规则服务的语法限制。在一个where块内,模式匹配中绑定的值在范围内,并且在where块中定义的值在该模式匹配内的守卫范围内。因此,where必须将块附加到至少可能存在模式匹配和保护的位置。这最终成为值声明和 case 表达式的分支。在您的第二个示例中,您试图将一个where块附加到任意表达式,这不是他们打算做的。

于 2021-11-30T21:59:47.647 回答