[Haskell-cafe] Use unsafePerformIO to catch Exception?

Lennart Augustsson lennart at augustsson.net
Wed Mar 25 08:12:01 EDT 2009


Some examples of what might happen:

If you have more than one possible exception in your code, you don't
know which one you will get.
It can vary between compilers, optimization levels, program runs, or
even evaluating the same expression within one program.

If you have code that have both an infinite loop and an exception you
don't know if you'll get the loop or the exception.

Breaking the deterministic behaviour can lead to strange consequences,
because the compiler relies on it.
For instance, the following code
   let x = someExpression
   print x
   print x
could print different values for x the two times you print it.
(This is somewhat unlikely, but could happen when evaluating in
parallel with ghc, because there is a small window where two threads
might start evaluating x and later update x with two different
values.)

  -- Lennart

On Wed, Mar 25, 2009 at 11:39 AM, Xiao-Yong Jin <xj2106 at columbia.edu> wrote:
> Jonathan Cast <jonathanccast at fastmail.fm> writes:
>
>> On Tue, 2009-03-24 at 23:13 -0700, Donn Cave wrote:
>>> Quoth Duncan Coutts <duncan.coutts at worc.ox.ac.uk>:
>>>
>>> > You must not do this. It breaks the semantics of the language.
>>> >
>>> > Other people have given practical reasons why you should not but a
>>> > theoretical reason is that you've defined a non-continuous function.
>>> > That is impossible in the normal semantics of pure functional languages.
>>> > So you're breaking a promise which we rely on.
>>>
>>> Could you elaborate a little, in what sense are we (?) relying on it?
>>>
>>> I actually can't find any responses that make a case against it on a
>>> really practical level - I mean, it seems to be taken for granted that
>>> it will work as intended,
>>
>> It shouldn't be.
>>
>> Consider:
>>
>>     loop = loop
>>     blam = error "blam"
>>     notReallyTry = unsafePerformIO . try . evaluate
>>
>> Now, normally, we have, for all x, y,
>>
>>       x `seq` y `seq` x
>>     = y `seq` x
>>
>> But we clearly do *not* have this for x = blam, y = loop, since the
>> equality isn't preserved by notReallyTry:
>>
>>     notReallyTry $ blam `seq` loop `seq` blam = Left (ErrorCall "blam")
>>     notReallyTry $ loop `seq` blam            = loop
>>
>> Now, say a compiler sees the definition
>>
>>     foo x y = x `seq` y `seq` x
>>
>> in one module, and then in a later one
>>
>>     expectToBeTotal = notReallyTry $ foo blam loop
>>
>> ?  What happens if the compiler, while compiling foo, notices that x is
>> going to be evaluated eventually anyway, and decides against forcing it
>> before y?
>>
>> What if foo was written as
>>
>>     foo (!x) (!y) = x
>>
>> ?  Which order are the evaluations performed in?  In a purely functional
>> language, it doesn't matter; but all of a sudden with impure operations
>> like notReallyTry popping up, it does.
>
> Could you elaborate more about why this kind of breakage
> wouldn't happen if 'try' is used in an IO monad as intended?
>
> Thanks.
> --
>    c/*    __o/*
>    <\     * (__
>    */\      <
> _______________________________________________
> Haskell-Cafe mailing list
> Haskell-Cafe at haskell.org
> http://www.haskell.org/mailman/listinfo/haskell-cafe
>


More information about the Haskell-Cafe mailing list