Solved: Typing problems with polymorphic recursion and
typeclasses
oleg@pobox.com
oleg@pobox.com
Tue, 6 May 2003 20:25:03 -0700 (PDT)
Martin SULZMANN wrote:
> Here's a simplified version.
> h :: (Show ha) => t -> ha
> h b = g (h b)
> > > g:: (Show ga) => ga -> gb
> > > g = undefined
In an attempt to discover the type signature of h, let us lift it:
> g:: (Show ga,Show gb) => ga -> gb
> g = undefined
> class (Show ha) => H t ha where
> h:: t->ha
> h b = g (h b)
gives us an error:
Could not deduce (H t ha1) from the context (H t ha)
Probable fix:
Add (H t ha1) to the class or instance method `h'
arising from use of `h' at /tmp/e.hs:10
In the first argument of `g', namely `(h b)'
In the definition of `h': g (h b)
However, if we change the class H into
> class (Show ha) => H t ha | t->ha where
> h:: t->ha
> h b = g (h b)
everything typechecks. It seems very hard for me to avoid the thought
that the type of the original h should be
forall t ha. (Show ha) => t -> ha | t -> ha
or, to re-write it into valid Haskell,
forall t ha. (H t ha) => t -> ha
That might answer my previous question...
Regarding your observation:
> g:: (Show ga) => ga -> gb
> g = undefined
> 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
> as my above calculation shows there shouldn't be a problem in the
> first place.
I think I can see the problem: if we have a function
foo:: t1 -> t2
then GHC definitely knows that foo relates t1 and t2 by a functional
dependency -- indeed, foo is a function.
However, when presented with a type signature
bar:: (C t3) => t3 -> t4
the compiler doesn't know if bar is a regular function, or if it is a
method in the class C. In either case, the signature is just the
same. If bar is a bona fide function, then t3 and t4 are related by a
functional dependency. If bar is a method of C, things are not so
clear. Well, they are clear for the single-parameter class C without
overlapping instances (which GHC actually allows). However, for
baz:: (D t5 t6 t7) => t6 -> t8
there could conceivably be instances of baz which relate the same t6
to two different t8. That's why we have to explicitly specify the
functional dependency.
I guess this might probably answer the original question Levent Erkok
posed three years ago.