Question

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.

Was it helpful?

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.

OTHER TIPS

l should be of same type in function replace: Define it like:

data L = LInt Integer| LString String

See, replace can't be a polymorphic function. It uses strict types. This types defined by operations:

Prelude> :t (+)
(+) :: (Num a) => a -> a -> a

and

Prelude> :t (:)
(:) :: a -> [a] -> [a]

and

Prelude> :t 'c'
'c' :: Char

To make replace polymorphic you have to make it of polymorphic functions.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top