I'm struggling with the semantics of where within do blocks, specifically with Test.Hspec. The following works:
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)
This does not:
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)
It fails with:
/mnt/c/haskell/chapter15/tests/ExampleSpec.hs:13:5: error: parse error on input ‘describe’
|
13 | describe "bar" $ do
| ^^^^^^^^
Am I doing something wrong or is this some kind of inherent limitation with where?
A
whereclause can only be attached to a function or case binding, and must come after the right hand side body.When the compiler sees
where, it knows that the RHS of yourspec = ...equation is over. Then it uses indentation to figure out how far the block of definitions inside thewhereextends (just the singlef = idin this case). Following that the compiler is looking for the start of the next module-scope definition, but an indenteddescribe "bar" $ dois not valid for the start of a definition, which is the error you get.You cannot randomly insert a
whereclause into the middle of a function definition. It only can be used to add auxiliary bindings in scope over the whole RHS of a binding; it cannot be used to add local bindings in scope for an arbitrary sub-expression.However there is
let ... in ...for exactly that purpose. And since you're usingdoblocks under eachdescribe, you can also use theletstatement (using the remainder of thedoblock to delimit the scope of the local bindings, instead of theinpart of thelet ... in ...expression). So you can do this instead: