Curious behaviour of irrefutable pattern.

Hi, consider this program:
module Main (f, main) where
f ~(a:as) = 1 + f as
main = print $ f (error "Foobar!")
Obviously, the program should result in an error - the irrefutable pattern of f always succeeds, so f calls itself recursively ad infinitum and the result is <<Loop>> or nontermination. Or so I thought. Running this program after compiling with ghc -O (with 6.4.2, 6.6 and 6.7) results in: a.out: Foobar! So the 'error "Foobar!"' got evaluated. The compiler somehow replaced one 'bottom' with another which is arguably allowed. I wasn't able to come up with an example where it turned a bottom into a value or vice versa. Anyway, this looks suspicious to me, so what's happening here? regards, Bertram

Strictness analysis generally treats non-termination and errors (calls to 'error') the same. It decides that f is strict (that is f bot = bot), and hence it can use call-by-value. In general, GHC (like every other compiler that does strictness analysis) feels free to change non-termination into a call to 'error' and vice versa. One could change that, but a lot of programs would become less efficient as a result. Simon | -----Original Message----- | From: glasgow-haskell-users-bounces@haskell.org [mailto:glasgow- | haskell-users-bounces@haskell.org] On Behalf Of Bertram Felgenhauer | Sent: 20 December 2006 09:04 | To: glasgow-haskell-users@haskell.org | Subject: Curious behaviour of irrefutable pattern. | | Hi, | | consider this program: | | > module Main (f, main) where | > | > f ~(a:as) = 1 + f as | > | > main = print $ f (error "Foobar!") | | Obviously, the program should result in an error - the irrefutable | pattern of f always succeeds, so f calls itself recursively ad | infinitum and the result is <<Loop>> or nontermination. | | Or so I thought. Running this program after compiling with ghc -O | (with 6.4.2, 6.6 and 6.7) results in: | | a.out: Foobar! | | So the 'error "Foobar!"' got evaluated. The compiler somehow replaced | one 'bottom' with another which is arguably allowed. I wasn't able to | come up with an example where it turned a bottom into a value or vice | versa. | | Anyway, this looks suspicious to me, so what's happening here? | | regards, | | Bertram | _______________________________________________ | Glasgow-haskell-users mailing list | Glasgow-haskell-users@haskell.org | http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

Simon Peyton-Jones wrote:
In general, GHC (like every other compiler that does strictness analysis) feels free to change non-termination into a call to 'error' and vice versa. One could change that, but a lot of programs would become less efficient as a result.
Just to clarify, I'm happy with that behaviour, I just found it surprising. I was looking for an explanation and got one. Thanks! Bertram

In general, GHC (like every other compiler that does strictness analysis) feels free to change non-termination into a call to 'error' and vice versa.
Under what circumstances does the (error -> non-termination) transformation happen? I'd be unhappy if (head "") put me into an infinite loop instead of giving me an error message. Do you mean if I have both an infinite loop and an error call, then I might get only the loop? thanks -m

| Do you mean if I have both an infinite loop and an error call, then I | might get only the loop? Absolutely. f x = error "urk" loop n = loop (n+1) main = print (f (loop 0)) The compiler figures out that f is strict, and uses call by value. Result is a loop. Simon

| Do you mean if I have both an infinite loop and an error call, then I | might get only the loop?
Absolutely.
That's fine and what I expected. I take it, then, that the answer to the question of "under what circumstances does the (error -> non-termination) transformation happen?" is that GHC can choose among different bottoms that are present in the program. It can't, however, willy-nilly convert my error calls to bottom. (Or something more precise along the same lines.) Or no? thanks -m

| I take it, then, that the answer to the question of "under what | circumstances does the (error -> non-termination) transformation | happen?" is that GHC can choose among different bottoms that are | present in the program. It can't, however, willy-nilly convert my | error calls to bottom. (Or something more precise along the same | lines.) Yes, that's right, good point. Simon

On 22/12/2006, at 7:14 PM, Simon Peyton-Jones wrote:
| I take it, then, that the answer to the question of "under what | circumstances does the (error -> non-termination) transformation | happen?" is that GHC can choose among different bottoms that are | present in the program. It can't, however, willy-nilly convert my | error calls to bottom. (Or something more precise along the same | lines.)
Yes, that's right, good point.
I've been following this thread with interest. But now I don't understand what the conclusion is. I thought Simon's earlier example showed that the semantics of GHC does not distinguish between an infinite loop and a call to error. Otherwise the strictness optimisation would be unsound. Mike, what do you mean by "willy nilly convert my error calls to bottom"? Cheers, Bernie.

Bernie Pope
Mike, what do you mean by "willy nilly convert my error calls to bottom"?
Simon Peyton-Jones
In general, GHC (like every other compiler that does strictness analysis) feels free to change non-termination into a call to 'error' and vice versa. One could change that, but a lot of programs would become less efficient as a result.
I was concerned by the "vice versa" conversion--from an error call to non-termination. If more than one bottoms (say, a non-termination and an error call) are present in my program, I'm fine with getting any one of them. If I have only an error call, though, I do want to see an error message. An infinite loop would be unhelpful. So, I would consider it an unfriendly "willy nilly" convertion for GHC to generate code for: import System.Environment ( getArgs ) main = getArgs >>= putStrLn . head that failed to terminate when I passed no command-line arguments. -m
participants (4)
-
Bernie Pope
-
Bertram Felgenhauer
-
Mike Gunter
-
Simon Peyton-Jones