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 <bjpop@csse.unimelb.edu.au> writes: ...
Mike, what do you mean by "willy nilly convert my error calls to bottom"?
Simon Peyton-Jones <simonpj@microsoft.com> writes:
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