On Tue, Jul 1, 2008 at 1:46 PM, <
oleg@okmij.org> wrote:
Hello!
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
I have tried the following code:
> data Tricky a = Tricky a Char deriving (Data,Typeable,Show)
> mapTricky :: (Data a,Data b) => (a -> b) -> Tricky a -> Tricky b
> mapTricky = gmap
> tricky1 = Tricky 'a' 'b'
> tr_test1 = mapTricky (=='a') tricky1
> tr_test2 = mapTricky (chr . (+1) . ord) tricky1
The expression tr_test1 prints
*GMap> tr_test1
Tricky True 'b'
as one would expect. However, tr_test2 prints:
*GMap> tr_test2
Tricky 'b' 'c'
Here the transforming function is applied to *both* the functor argument and the Char value.
The argument of gmap is not only applied to the functor argument but also to 'b' in "tricky1". This problem was already pointed out by Claus.
It is important to remark that although gmap passes the test in the benchmark, it does not behave like a functorial map. Furthermore, this trick cannot be reused to implement the crush test.
Nevertheless, it is surprising (to me at least) that you can implement something close to type changing map, using only SYB's cast operation.
I am going to update the "Comparing libraries" paper to refer to your and Claus' variants of gmap.
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.
Yes. The more flexible type turns what would have been type errors into strange runtime behaviour:
*GMap> gmap (id::Maybe () -> Maybe ()) (Just ()) :: Bool
*** Exception: SYB1_2/GMap.lhs:(20,26)-(21,45): Non-exhaustive patterns in case
*GMap> gmap (id::Maybe () -> Maybe ()) (Nothing::Maybe ()) :: Bool
False
Cheers,
Alexey