0

I ran into a Haskell compilation error and it can be simplified to the following example:

newtype MyNewType a = MyNewType { run :: Maybe a } -- just for demo

class ExTypeClass g where
  bar :: String -> g ()

instance ExTypeClass MyNewType where
  bar s = MyNewType $ Just ()

-- foo :: ExTypeClass g => g ()
foo = bar "\n"

The above code causes the following error during compilation:

• Ambiguous type variable ‘g0’ arising from a use of ‘bar’
  prevents the constraint ‘(ExTypeClass g0)’ from being solved.
  Relevant bindings include foo :: g0 () (bound at TypeClass.hs:10:1)
  Probable fix: use a type annotation to specify what ‘g0’ should be.
  These potential instance exist:
    instance ExTypeClass MyNewType -- Defined at TypeClass.hs:6:10
• In the expression: bar "\n"
  In an equation for ‘foo’: foo = bar "\n"

If I added the foo :: ExTypeClass g => g () back then it works fine.

However if I remove foo = bar "\n", the program can compile and get loaded in GHCI, then check bar "\n" type in the terminal:

*Main> :t bar "\n"
bar "\n" :: ExTypeClass g => g ()

My questions are

1) Why couldn't it compile in the first place? Isn't Haskell highly polymorphic to begin with? Why do I have to specify type variable g0 anyway for it to compile? I think I don't understand something fundamentally important.

2) How come GHCI can deduce the type but not when I wrote it in the program?

Thank you very much in advance.

dhu
  • 718
  • 6
  • 19
  • 1
    I think the relevant phenomenon is the monomorphism restriction but I would have expected a different error message... https://stackoverflow.com/questions/32496864/what-is-the-monomorphism-restriction – Li-yao Xia Jun 09 '20 at 03:34

1 Answers1

1

This is due to the monomorphism restriction. Basically, if you want to define typeclass-polymorphic (aka ad-hoc polymorphic) top-level bindings, you should provide a type signature with proper class constraints. Otherwise Haskell language may consider, often for good reasons, that you want them to be typeclass-monomorphic, that means specialized on one instance. The compiler must know on which instance to specialize, and will fail if :

  • you don't use the binding at all, because the typechecker does not know on which type the binding should be specialized. In some cases it might use defaulting rules to pick one of them as a guess.
  • you use the binding with different types, because the typechecker will specialize on one, and fail on the other.

You can lift this restriction, however it is more advisable to provide explicit class constraints on top-level bindings if you wan them class-polymorphic. For convenience, GHCI defaults to disable monomorphism restriction.

Paul R
  • 747
  • 4
  • 13