unsafeCoerce shenanigans

Previous Topic Next Topic
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view

unsafeCoerce shenanigans

David Feuer
Suppose I have a function of type

unionWith# :: (Hashable k, Eq k) => (a -> a -> (# a #)) -> HashMap k a
-> HashMap k a -> HashMap k a

I can use this to implement strict and lazy unions with practically no
code duplication either in source or in generated code:

S.unionWith, L.unionWith ::
  (Hashable k, Eq k)
  =>  (a -> a -> a) -> HashMap k a -> HashMap k a -> HashMap k
S.unionWith f = unionWith# (\a b -> let !r = f a b in (# r #))
L.unionWith f = unionWith# (\a b -> (# f a b #))

Semantically, this gets the job done, but there's a potential
performance problem. If the function passed to either version of
unionWith fails to inline, then we'll build a closure to interface
with the underlying function. This is not so great, because it adds
indirection in a likely hot spot. I'm not too terribly worried about
the lazy version, because it's prone to leaking memory anyway, but I
do care about the strict version. It seems to me that for the *strict*
version, I can probably use unsafeCoerce to do the job instead:

S.unionWith f = unionWith# (unsafeCoerce f)

When unionWith# applies f to values, it will get back something that
it *thinks* might be a thunk, but actually will never be a thunk. We
don't really care, especially because we just stick it into a lazy
constructor field.

My main concern is that this is obviously not at all an officially
supported way to use unsafeCoerce. What are the chances that GHC will
do something that breaks it? If I can't do this reliably, I should
still be able to use unionWith# to reduce *source* size, relying on
lots of inlining to avoid closures, but the generated code would stay

Glasgow-haskell-users mailing list
[hidden email]