Reputation: 2738
Suppose I have a typeclass, two instances, and a function defined as follows.
class C c where
newC :: String -> c
showC :: c -> String
data C1 = C1 String
instance C C1 where
newC string = C1 string
showC (C1 string) = string
data C2 = C2 String
instance C C2 where
newC string = C2 string
showC (C2 string) = string
topFn :: String -> String
topFn = showC . newC
When loaded, GHC produces an "Ambiguous type" error,
Ambiguous type variable ‘c0’ arising from a use of ‘showC’
prevents the constraint ‘(C c0)’ from being solved.
Probable fix: use a type annotation to specify what ‘c0’ should be.
These potential instances exist:
instance C C1 -- Defined at ambigType.hs:8:10
instance C C2 -- Defined at ambigType.hs:14:10
I understand that the intermediate value could be either C1
or C2
and that without further information GHC has no way to determine which it should be.
Writing two versions of topFn
solves the problem.
topFn1 :: String -> String
topFn1 = showC . (newC :: String -> C1)
topFn2 :: String -> String
topFn2 = showC . (newC :: String -> C2)
But since these are essentially identical functions, this seems like an ugly solution. Is there a better way?
Thanks.
Upvotes: 1
Views: 63
Reputation: 32319
Yes, use the TypeApplications
, AllowAmbiguousTypes
, ExplicitForall
, and ScopedTypeVariables
extensions. Then, modify topFn
to be
topFn :: forall a. C a => String -> String
topFn = showC . newC @a
Then, you can call topFn
using an explicit type-application: topFn @C1 "calling the C1 variant"
and topFn @C2 "calling the C2 variant"
. If you want, you can define:
topFn1 = topFn @C1
topFn2 = topFn @C2
Upvotes: 3