Bug in SYB, SYB-documentation or GHC-API (or I messed up somewhere)

Dear Haskelers, Since I don't quite know whether this is a SYB-thing or a GHC-specific thing, I figured this report should go here. If not, kindly redirect. I am trying to write a program, using the GHC-API to refactor some parts of the GHC-code (initially, I want to add a type variable to the types in the AST to express the representation of types, as noted to be a Good Thing at [1]). To do this, I first search for all definitions of and references to type constructors. I do this using SYB. Consider these functions: type OccurrenceTable = [(RdrName, (SrcSpan,Bool))] locTyNames :: SrcSpan -> Bool -> HsType RdrName -> OccurrenceTable locTyNames l p (HsTyVar n ) = [(n, (l, p))] <… HSType-specific traversal cases omitted for brevity …> extTyNamesDef :: Data a => SrcSpan -> a -> OccurranceTable extTyNamesDef l x = gmapQl (++) [] (extTyNames l) x extTyNamesTy :: SrcSpan -> HsType RdrName -> OccurranceTable extTyNamesTy l x = locTyNames l True x extTyNames l x = (extTyNamesDef l x `mkQ` extTyNamesTy l) x The OccurrenceTable type collects for every type constructor (RdrName) the location of its occurrence (either definition or reference, SrcSpan) and whether adding a type variable (formal or actual) requires parenthesis (Bool). This works fine, although now, we always find the same SrcSpan for everything. When traversing an AST, whenever we find anything that's Located, we want to continue the traversal with this new location information. To this end, we define: extTyNamesLoc :: Data a => Located a -> OccurrenceTable extTyNamesLoc (L l x) = extTyNames l x and modify the universal entry point to: extTyNames l x = (extTyNamesDef l x `mkQ` extTyNamesTy l `ext1Q` extTyNamesLoc) x However, it turns out that this never leads to an evaluation of extTyNamesLoc. I've come as far as to see that, since "Located a" is a synonym for "GenLocated SrcSpan a", the type we're looking for really has a binary constructor, thus we should use ext2Q. This made things work: We first modify the function we apply to Located things: extTyNamesLoc :: (Data loc, Data a) => SrcSpan -> GenLocated loc a -> OccurrenceTable extTyNamesLoc l (L l' x) = case cast l' of Just l'' -> extTyNames l'' x Nothing -> extTyNames l x and define the universal entry point to be: extTyNames l x = (extTyNamesDef l x `mkQ` extTyNamesTy l `ext2Q` extTyNamesLoc l) x Even though I see why this works, I'm not completely sure why the former version does not. Is there no currying with type constructors (for SYB)? The cast I perform always succeeds, because all types that occur in the traversal are "Located x" and never "GenLocated NonSrcSpanType x". The extensions to extX functions (i.e. ext1Q, ext2Q, ext1T, etc.) are not very extensively documented. Their discussion in the ICFP04 paper only discusses their relation to TypeableX classes, but not to the partiality of type constructor application and/or synonyms. (As a side-note on that discussion in ICFP04, the remark that we're generally interested in type constructors with arguments of kind * sounded fine, until I started to try and parameterise the AST in the location-annotation-type as well; it made my brain hurt.) Apologies for the waffling, but comments would be greatly appreciated. Regards, Philip [1] http://hackage.haskell.org/trac/ghc/wiki/Commentary/Compiler/HsSynType

Hi Philip, On Fri, May 11, 2012 at 5:12 PM, "Philip K. F. Hölzenspies" < pkfh@st-andrews.ac.uk> wrote:
However, it turns out that this never leads to an evaluation of extTyNamesLoc. I've come as far as to see that, since "Located a" is a synonym for "GenLocated SrcSpan a", the type we're looking for really has a binary constructor, thus we should use ext2Q.
This made things work: We first modify the function we apply to Located things:
extTyNamesLoc :: (Data loc, Data a) => SrcSpan -> GenLocated loc a -> OccurrenceTable extTyNamesLoc l (L l' x) = case cast l' of Just l'' -> extTyNames l'' x Nothing -> extTyNames l x
Do you really need this? Can't you use the definition of `extTyNamesLoc` shown previously, and redefine `extTyNames` to use `ext2Q`, as in:
extTyNames l x = (extTyNamesDef l x `mkQ` extTyNamesTy l `ext2Q` extTyNamesLoc l) x
? Cheers, Pedro

Dear Pedro, On 13 May 2012, at 20:29, José Pedro Magalhães wrote:
On Fri, May 11, 2012 at 5:12 PM, "Philip K. F. Hölzenspies"
wrote: However, it turns out that this never leads to an evaluation of extTyNamesLoc. I've come as far as to see that, since "Located a" is a synonym for "GenLocated SrcSpan a", the type we're looking for really has a binary constructor, thus we should use ext2Q.
This made things work: We first modify the function we apply to Located things:
extTyNamesLoc :: (Data loc, Data a) => SrcSpan -> GenLocated loc a -> OccurrenceTable extTyNamesLoc l (L l' x) = case cast l' of Just l'' -> extTyNames l'' x Nothing -> extTyNames l x
Do you really need this? Can't you use the definition of `extTyNamesLoc` shown previously, and redefine `extTyNames` to use `ext2Q`, as in:
As it turns out, yes I do. You had me doubting whether I had tried this, but doing such a thing gives me: refact.hs:124:72: Could not deduce (d1 ~ SrcSpan) from the context (Data a) bound by the type signature for extTyNames :: Data a => SrcSpan -> RefCtx -> a -> OccTab at refact.hs:124:1-91 or from (Data d1, Data d2) bound by a type expected by the context: (Data d1, Data d2) => GenLocated d1 d2 -> OccTab at refact.hs:124:21-88 `d1' is a rigid type variable bound by a type expected by the context: (Data d1, Data d2) => GenLocated d1 d2 -> OccTab at refact.hs:124:21 Expected type: GenLocated d1 d2 -> OccTab Actual type: Located d2 -> OccTab In the return type of a call of `extTyNamesLoc' In the second argument of `ext2Q', namely `extTyNamesLoc l r' Which makes a certain amount of sense. The use of ext2Q binds d1 and d2 to be restricted to Data, but otherwise unspecific. This seems at odds with the intention of extXY, hence my confusion. I have since found and come to understand extB, though. This makes the implementation of extTyNamesLoc much, much nicer. However, I still think finding a good explanation for the behaviour of ext1Q/ext2Q could lead to a helpful addendum of the SYB documentation. Regards, Philip
participants (3)
-
"Philip K. F. Hölzenspies"
-
José Pedro Magalhães
-
Philip K. F. Hölzenspies