问题
I'm learning Haskell and I've written this function:
safeHead :: [a] -> Maybe a
safeHead [] = Nothing
safeHead (x:xs) = Just x
I'm now trying to test it with HSpec:
import Test.Hspec
main :: IO ()
main = hspec spec
spec :: Spec
spec =
describe "safeHead" $
it "should return Nothing for empty list" $
safeHead [] `shouldBe` Nothing
But that fails to compile:
Error:(14, 19) ghc: No instance for (Eq a0) arising from a use of ‘shouldBe’
The type variable ‘a0’ is ambiguous
Note: there are several potential instances:
instance Eq a => Eq (Maybe a) -- Defined in ‘GHC.Base’
instance Eq a => Eq (GHC.Real.Ratio a) -- Defined in ‘GHC.Real’
instance Eq Ordering -- Defined in ‘ghc-prim-0.4.0.0:GHC.Classes’
...plus 31 others
In the second argument of ‘($)’, namely
‘safeHead [] `shouldBe` Nothing’
In the second argument of ‘($)’, namely
‘it "should return Nothing for empty list"
$ safeHead [] `shouldBe` Nothing’
In the expression:
describe "safeHead"
$ it "should return Nothing for empty list"
$ safeHead [] `shouldBe` Nothing
I have also tried this:
safeHead :: (Eq a) => [a] -> Maybe a
safeHead [] = Nothing
safeHead (x:xs) = Just x
But that still fails with:
Error:(14, 19) ghc: No instance for (Eq a0) arising from a use of ‘shouldBe’
The type variable ‘a0’ is ambiguous
Note: there are several potential instances:
instance (Eq a, Eq b) => Eq (Either a b)
-- Defined in ‘Data.Either’
instance Eq Data.Monoid.All -- Defined in ‘Data.Monoid’
instance forall (k :: BOX) (f :: k -> *) (a :: k).
Eq (f a) =>
Eq (Data.Monoid.Alt f a)
-- Defined in ‘Data.Monoid’
...plus 43 others
In the second argument of ‘($)’, namely
‘safeHead [] `shouldBe` Nothing’
In the second argument of ‘($)’, namely
‘it "should return Nothing for empty list"
$ safeHead [] `shouldBe` Nothing’
In the expression:
describe "safeHead"
$ it "should return Nothing for empty list"
$ safeHead [] `shouldBe` Nothing
I don't know what's the problem here. If I try other tests like these, it compiles fine:
it "should return the head" $ do
safeHead [1] `shouldBe` Just 1
safeHead [2,3,4,5,6,1] `shouldBe` Just 2
So, it's something about Nothing
itself, that it cannot be compared by equals? How do you assert that something returns Nothing
then? Or is my function too generic?
SIDE NOTE: I've seen a similar error with this function:
palindrome :: (Eq a) => [a] -> [a]
palindrome xs = xs ++ reverse xs
When trying to test for empty lists:
palindrome [] `shouldBe` []
Which fails with:
Error:(26, 21) ghc: No instance for (Eq a0) arising from a use of ‘shouldBe’
The type variable ‘a0’ is ambiguous
Note: there are several potential instances:
instance Eq a => Eq (Maybe a) -- Defined in ‘GHC.Base’
instance Eq a => Eq (GHC.Real.Ratio a) -- Defined in ‘GHC.Real’
instance Eq Ordering -- Defined in ‘ghc-prim-0.4.0.0:GHC.Classes’
...plus 32 others
In a stmt of a 'do' block: palindrome [] `shouldBe` []
In the second argument of ‘($)’, namely
‘do { palindrome [] `shouldBe` [] }’
In the second argument of ‘($)’, namely
‘it
"should turn a list into a palindrome, so it reads same both forwards and backwards"
$ do { palindrome [] `shouldBe` [] }’
回答1:
So, it's something about Nothing itself, that it cannot be compared by equals?
What's Nothing
's type? It's Nothing :: Maybe a
. And GHC doesn't like a
in this context: "The type variable ‘a0’ is ambiguous". After all, shouldBe
takes anything that can be compared with (==)
and shown. And Maybe a
is an instance of Eq
if a
is an instance of Eq
. GHC can't possibly know which a
you want to use, so you need to specify it by hand:
describe "safeHead" $
it "should return Nothing for empty list" $
safeHead [] `shouldBe` (Nothing :: Maybe Int)
That's not coercion, you're just making clear which of all possible types you want to use. Other examples:
describe "safeHead" $
it "should return Nothing for empty list" $ do
safeHead [] `shouldBe` (Nothing :: Maybe Int)
safeHead [] `shouldBe` (Nothing :: Maybe ())
safeHead [] `shouldBe` (Nothing :: Maybe Integer)
safeHead [] `shouldBe` (Nothing :: Maybe Char)
来源:https://stackoverflow.com/questions/30612654/hspec-nothing-expectation-failing-to-compile