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.
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.