
Hi Oleg!
It has occurred to me a year ago that the type-changing gMap is easily possible in SYB. The recent discussion has prompted me to implement that solution. I have committed a patch to
http://darcs.haskell.org/generics/comparison/SYB1_2/GMap.lhs
with the implementation. I hope this is OK: the previous version of that file was a stub saying that gMap is not supported. The function gMap has the expected type gmap :: (Data a, Data b, Data x, Data y) => (a -> b) -> x -> y
Thanks, looks nice. I suspected there had to be a gfoldl+gunfoldl- based approach, but I couldn't quite wrap my head around it. So simple in hindsight (given the proper auxiliary type constructors), and I might have got there in the end, but it was certainly not "easily possible" for me - so thank you for freeing me from the daily headache of trying yet another approach!-) A slight issue: this version does not distinguish functor parameter from equivalent types: *GMap> gmap not (True,True)::(,) Bool Bool (False,False) Perhaps we can combine our versions to get the best of both?
There are no unsafe operations used; there is not a single occurrence of insafePerformIO and the ilk. Incidentally, Data.Generics could, in principle at least, be implemented using a safe cast. Of course gmap is not a total operation: one can't really expect (gmap id True) to produce a character. It seems that gunfold is not total anyway.
The latter is the reason why this version is safer than mine, I think: 'instance Data (a->b)' implements a fake gfoldl, but turns uses of gunfoldl into runtime errors. Yet another reason why those instances are dubious, but any attempt to fake gunfoldl would have to involve error anyway, it seems (something like 'z (const (error "no such function"))' when grabbing an (a->b) out of thin air). So your version does at least get a proper runtime error in the cases where mine runs right out of type safety..
Regarding the instance of Data for functions: SmashA does define (co-variant) traversal `under lambda'. For example, we can (gmap fromEnum) on a function and convert (Bool->Bool) to (Bool->Int). Please see testt3 in
http://darcs.haskell.org/generics/comparison/SmashA/Syb4A.hs
I do not claim to have any use for that transformation, but it was easy to implement...
I was just surprised that the comparison seemed to make no mention of higher-order (as opposed to higher-kinded) types. Cheers, Claus