Solved: Typing problems with polymorphic recursion and typeclasses

Martin SULZMANN sulzmann@comp.nus.edu.sg
Tue, 6 May 2003 10:05:14 +1000 (EST)


 > First, let us distill the problem to the following few lines:
 > 
 > > f:: (Show fa) => fb -> fa
 > > f = undefined
 > >
 > > g:: (Show ga) => ga -> gb
 > > g = undefined
 > >
 > > -- h:: (Show fa) => t -> fa
 > > h b = f (g (h b))
 > 

Here's a simplified version. 

h :: (Show ha) => t -> ha
h b = g (h b)

Same problem occurs. Though this is strange.

Consider type inference for 

h b = g (h b)

We generate the following constraints:

th = tb -> ga, Show ga  -- results from g (h b)
th = tb -> gb           -- taking into account h b = g (h b)

In case of 

h :: (Show ha) => t -> ha
h b = g (h b)

we additionally generate

th = t -> ha, Show ha

Overall, we find

 th = tb -> ga, Show ga 
 th = tb -> gb,
 th = t -> ha, Show ha

(*) 


Note that this implies ga=ha, gb=ha

The error message 

    Ambiguous type variable(s) `ha' in the constraint `Show ha'
    arising from use of `g' at HaskellMay03.hs:16
    In the definition of `h'': g (h' b)

suggests that ghc does not apply the unifier ha=ga.
I suspect the type computed out of the constraints from (*) is 
as follows:

(Show ga, Show ha) => t -> ha

Indeed hugs says

ERROR HaskellMay03.hs:13 - Cannot justify constraints in explicitly typed binding
*** Expression    : h
*** Type          : Show a => b -> a
*** Given context : Show a
*** Constraints   : Show c

That is, the type (Show ga, Show ha) => t -> ha does not conform
to the annotation (Show ha) => t -> ha.


In case we use scoped type variables

h2:: (Show t2) => t1 -> t2
h2 b :: g1a
  = g ((h2 b)::g1a)

we impose some additional constraints (ga=g1a, ha=g1a). This seems to 
resolve the problem, though, as my above calculation shows there
shouldn't be a problem in the first place.

E.g. Chameleon accepts without problem

h :: (Show ha) => t -> ha
h b = g (h b)

or

h :: (Show ha) => t -> ha
h b = g ((h b)::ha)

This is the Chameleon equivalent to h2. Scoped type variables in
Chameleon refer to type annotations.

You can try this out by calling

chameleon -d HaskellMay03.hs

where

HaskellMay03.hs =

hconstraint Show  -- Chameleon does not know the Prelude.
                  -- We have to introduce type class constraints
                  -- explicitely 

g:: (Show ga) => ga -> gb
g = undefined

h :: (Show ha) => t -> ha
h b = g (h b)

h' :: (Show ha) => t -> ha
h' b = g ((h' b)::ha)


Martin