Reputation: 15481
Say I have a function:
safeHead :: [a] -> Maybe a
safeHead [] = Nothing
safeHead xs = Just $ head xs
And a test:
describe "Example.safeHead" $ do
it "returns the head" $ do
safeHead [1,2,3] `shouldBe` Just 1
it "returns Nothing for an empty list" $
safeHead [] `shouldBe` Nothing
This however produces:
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 forall (k :: BOX) (s :: k). Eq (Data.Proxy.Proxy s)
-- Defined in ‘Data.Proxy’
instance (GHC.Arr.Ix i, Eq e) => Eq (GHC.Arr.Array i e)
-- Defined in ‘GHC.Arr’
...plus 88 others
In the second argument of ‘($)’, namely
‘safeHead [] `shouldBe` Nothing’
In a stmt of a 'do' block:
it "returns Nothing for an empty list"
$ safeHead [] `shouldBe` Nothing
In the second argument of ‘($)’, namely
‘do { it "returns the head"
$ do { safeHead [...] `shouldBe` Just 1 };
it "returns Nothing for an empty list"
$ safeHead [] `shouldBe` Nothing }’
Why? And how can I fix it?
Upvotes: 3
Views: 868
Reputation:
This code tests a Maybe Monad that returns a "Just" value:
import Test.Tasty
import Test.Tasty.HUnit
import Data.List
main = defaultMain tests
tests = testGroup "Tests uncons form Data.List"
[testCase "Show uncons [1,2,3,4] = (1, [2,3,4])" $
Just (1,[2,3,4]) @=? (uncons [1,2,3,4])]
Upvotes: 0
Reputation: 13876
As user2407038 commented, compiler doesn't know how to instantiate a
. The fix he proposed is probably the best one -- you should specify type of a
explicitly.
But for completeness I'd like to note, that there is other solution, extended default rules:
{-# LANGUAGE ExtendedDefaultRules #-}
describe "Example.safeHead" $ do
it "returns the head" $ do
safeHead [1,2,3] `shouldBe` Just 1
it "returns Nothing for an empty list" $
safeHead [] `shouldBe` Nothing
The extension modifies the standard defaulting rules to include more cases, e.g. Eq
type class.
Added: After some thinking I reconsider my answer a bit. Results of unit testing of polymorphic functions should not depend of a particular way to instantiate type variables. So probably extended default rules is The Right Thing in tests? I never used them it real code, so I can't say for sure, but it definitely worth thinking about.
Upvotes: 4
Reputation: 105935
shouldBe :: (Eq a, Show a) => a -> a -> Expectation
safeHead :: [a] -> Maybe a
[] :: [a]
safeHead [] :: Maybe a
Nothing :: Maybe a
shouldBe (safeHead []) :: (Eq a, Show a) => Maybe a -> Expectation
shouldBe (safeHead []) Nothing :: Expectation -- but what's `a`?
As you can see, a
is completely ambiguous. It can a be any type that has Show
and Eq
instances. This is also part of your error message:
No instance for (Eq a0) arising from a use of ‘shouldBe’ The type variable ‘a0’ is ambiguous
So pick one:
it "returns Nothing for an empty list" $
safeHead [] `shouldBe` (Nothing :: Maybe ())
While you're at it, use QuickCheck to verify that safeHead
works like head
:
it "returns Just (head xs) for a non-empty list" $ property $ \(NonEmpty xs) ->
safeHead xs `shouldBe` (Just (head xs) :: Maybe Integer)
Upvotes: 2