I am getting type inference errors because GHC will not infer a constraint variable. It looks inferable by first-order unification. In further investigation, I found that inserting let-bindings changes the behavior of type inference. I'd like to know what GHC is doing.
The code here demonstrates the problem. The newtype ConstrainedF c
stands for a polymorphic function whose type parameter is constrained by c
. As far as I can tell, GHC won't infer c
based on the values given to ConstrainedF
.
{-# LANGUAGE RankNTypes, ScopedTypeVariables, ConstraintKinds, MonoLocalBinds #-}
import Data.Monoid
import GHC.Prim(Constraint)
newtype ConstrainedF c =
ConstrainedF { runConstrainedF :: forall a. c a => [a] -> a}
applyConstrainedF :: forall c a. c a => ConstrainedF c -> [a] -> a
applyConstrainedF f xs = runConstrainedF f xs
-- GHC cannot infer the type parameter of ConstrainedF
foo :: [Int]
foo = applyConstrainedF (ConstrainedF mconcat) [[1], [2]]
--foo = applyConstrainedF (ConstrainedF mconcat :: ConstrainedF Monoid) [[1], [2]]
It should be possible to infer types in the application ConstrainedF mconcat
:
ConstrainedF
has typeforall c. (forall a. c a => [a] -> a) -> ConstrainedF c
.mconcat
has typeforall b. Monoid b => [b] -> b
.forall b. Monoid b => [b] -> b
unifies withforall a. c a => [a] -> a
by the assignmenta := b
andc := Monoid
.
However, GHC complains:
Could not deduce (Monoid a) arising from a use of `mconcat'
from the context (c0 a).
What rules do I have to follow regarding constraint variables so that GHC can infer types?
A typical solution for ambiguous type errors is to add proxy values to constrain the ambiguous type. This was finicky when I tried it. If I just add an extra parameter to constrain the type of c
, it works:
data Cst1 (c :: * -> Constraint) = Cst1
monoid :: Cst1 Monoid
monoid = Cst1
applyConstrainedF :: forall c a. c a => ConstrainedF c -> Cst1 c -> [a] -> a
applyConstrainedF f _ xs = runConstrainedF f xs
foo :: [Int]
foo = applyConstrainedF (ConstrainedF mconcat) monoid [[1], [2]]
But introducing a let binding in foo
confuses type inference, and it can no longer unify c
with Monoid
.
foo_doesn't_work :: [Int]
foo_doesn't_work = let cf = ConstrainedF mconcat
in applyConstrainedF cf monoid [[1], [2]]
Since type inference gets the right answer in one of these two functions, this tells me that GHC will unify constraint variables in some situations but not others. I don't understand what's going on.
The problem here is subtyping. In your example,
c
could as well be(Monoid b, Eq b)
.Furthermore, you could then use
Data.Typeable
to inspect whatc
was instantiated with.Or, what if you asked to "unify"
(c, d)
(a pair of constraints) withMonoid
?The answer to the second part of your question is — you guessed it! — let generalization.
I know that you guessed it since you've added a
MonoLocalBinds
pragma. However, it doesn't do what you expect here. You see, it only stops generalization of truly local bindings — ones that depend on function parameters or other local bindings.E.g. this works:
For the details see Let generalisation: Which bindings are affected?