
Given: data D a = A a | B Int Char dmapNoCoerce :: (a -> b) -> D a -> D b dmapNoCoerce f (A a) = A (f a) dmapNoCoerce _ (B i c) = B i c I have to reconstruct a B change it from D a to D b. But at the lower level, couldn't this be implemented as a type cast? What prevents such an optimization? I can write this as dmapCoerce :: (a -> b) -> D a -> D b dmapCoerce f (A a) = A (f a) dmapCoerce _ b@(B {}) = Unsafe.Coerce.unsafeCoerce b
From the core, it looks like dmapCoerce indeed has a cast with no allocation, while dmapNoCoerce allocates a new B.
It seems to work, but is it safe? Is there a more principled way to do it? I can't convince Data.Coerce to cooperate, presumably because 'a' and 'b' are not coercible themselves, and it doesn't believe me if I try to tell it the type role is phantom.