Search code examples
haskellgeneric-programming

Ambiguous type variable


Related to my earlier question on traversing data structures, I'm having a problem making my code generic when I use it along with the uniplate package. I'm dealing with the data structures in the Language.Exts.Annotated.Syntax module, which are all generic with a type parameter l. This l is the same throughout the tree.

The kind of code I'm writing is like this:

doInt :: Child1 l -> Child1 l
doInt (Child1 l n) = Child1 l (n + 1)

doString :: Child2 l -> Child2 l
doString (Child2 l (_:s)) = Child2 l ('j' : s)

replace :: Data l => Parent l -> Parent l
replace = transformBi doInt
        . transformBi doString

This code produces the following error on both of the last two lines:

Ambiguous type variable `l' in the constraint:
  `Data l' arising from a use of `transformBi' at Test.hs:31:10-52
Probable fix: add a type signature that fixes these type variable(s)

I can see why this code is ambiguous: transformBi accepts a (to -> to) and from and turns it into a from; in my case there's no link between the l in Child1 l and the l in Parent l. What I don't see is how to fix it. I've tried adding a type constraint like transformBi (doInt :: Child1 l -> Child1 l), but I get the same error; it's as if I'm introducing a new l when I do this.

How can I tell the compiler that I'm using the same l for replace, transformBi doInt and transformBi doString?

Edit: Here is the full program that demonstrates what I'm doing. Under GHC 6.10.4, this program fails to compile, with the above error.


Solution

  • It looks like you need the scoped type variables extension.

    {-# LANGUAGE ScopedTypeVariables #-}
    
    replace :: forall l. Data l => Parent l -> Parent l
    replace = transformBi (doInt :: Child1 l -> Child1 l)
            . transformBi (doString :: Child2 l -> Child2 l)
    

    Note that quantification must be explicit to bring l into scope.