为什么我不能在 Hspec 中找到“在哪里”工作

Why cannot I get `where` to work in Hspec

我在 do 块中与 where 的语义作斗争,特别是 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 的某种固有限制?

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

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

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

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

然而 let ... in ... 正是为了这个目的。并且由于您在每个 describe 下使用 do 块,您还可以使用 let 语句(使用 do 块的其余部分来分隔本地的范围绑定,而不是 let ... in ... 表达式的 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)