[Haskell-cafe] RE: [Haskell] unsafePerformIO and optimizations

Simon Marlow simonmar at microsoft.com
Mon Aug 8 05:33:01 EDT 2005

On 06 August 2005 15:24, Wolfgang Jeltsch wrote:

> First, what is the problem with inlining a function call?  Say, I
> have a function f defined as follows:
>     f :: Char -> ()
>     f c = unsafePerformIO (putChar c)
> If inlining is allowed, the expression f '*' becomes
>     unsafePerformIO (putChar '*').
> Is this a problem?  Or is inlining dangerous in other situations?
> On the other hand, I can see that inlining non-functions could cause
> harm. Say I have something like this:
>     u :: ()
>     u = unsafePerformIO (putChar '*')
> Now, inlining u would transform (u,u) to
>     (unsafePerformIO (putChar '*),unsafePerformIO (putChar '*'))
> which could result in putChar '*' being executed multiple times.  So
> why does the library documentation only talk about disabling inlining
> of functions? 

Well, you're right - it's all about sharing of redexes of the form
(unsafePerformIO e).  When you write an unsafePerformIO with a side
effect, you probably have an intuition about when you expect that redex
to be reduced.  In your example above, you're probably thinking "it'll
be executed whenever f is called".  However, optimisations can
invalidate your intuition in many ways, by increasing (or reducing)
sharing of the unsafePerformIO redex.

Inlining 'f' itself isn't a problem, because that doesn't change
sharing, as you pointed out.  However, it might expose potential for
*reducing* sharing, because (unsafePerformIO (putChar '*')) is now a
visibly constant expression, it only needs to be evaluated once per run
of the program.

> I understand that common subexpression elimination could cause harm. 
> It could transform the expression
>     (unsafePerformIO (putChar '*),unsafePerformIO (putChar '*'))
> to
>     let
>         u = unsafePerformIO (putChar '*')
>     in
>     (u,u),
> couldn't it?  This would result in the I/O action be performed at
> most once which is not what was intended.  But couldn't the same
> thing also happen if I use the expression (f '*',f '*'), probably in
> a different module?

Well, quite.

> Does this mean that I have to use -fno-cse not
> only in the module which contains the unsafePerformIO application but
> also in every other module which uses unsafePerformIO indirectly via
> f or functions using f? 

This way leads to madness - GHC's optimisations are simply not designed
to provide any kind of guarantee about maintaining sharing properties.
The guidelines we've given for unsafePerformIO are enough to cover
simple uses like global variables.  We should probably amend the
documentation to say this.


More information about the Haskell-Cafe mailing list