[Haskell-cafe] ($) not as transparent as it seems

Daniel Fischer daniel.is.fischer at googlemail.com
Thu Feb 3 23:25:08 CET 2011

On Thursday 03 February 2011 23:03:36, Luke Palmer wrote:
> This is probably a result of strictness analysis.  error is
> technically strict, so it is reasonable to optimize to:
>     let e = error "foo" in e `seq` error e

I think so too.

module Errors where

foo = error (error "foo")

bar = error $ error "bar"

produces the core
Errors.bar :: forall a_aaN. a_aaN
Errors.bar =
  \ (@ a_aaN) ->
      @ [GHC.Types.Char]
      @ a_aaN
      (GHC.Err.error @ a_aaN)
      (GHC.Err.error @ [GHC.Types.Char] (GHC.Base.unpackCString# "bar"))

a_rb8 :: [GHC.Types.Char]
[GblId, Str=DmdType b]
a_rb8 =
  GHC.Err.error @ [GHC.Types.Char] (GHC.Base.unpackCString# "foo")

Errors.foo :: forall a_aaP. a_aaP
Errors.foo =
  (\ (@ a_aaP) -> a_rb8)
  `cast` (forall a_aaP. CoUnsafe [GHC.Types.Char] a_aaP
          :: (forall a_aaP. [GHC.Types.Char]) ~ (forall a_aaP. a_aaP))

The first argument to ($) is evaluated before the second [because the 
function may be lazy), resulting in the start of the error message 
"***Exception: ", then that error-call must evaluate its argument, error 
"bar", which results in "***Exception: bar" (and terminates the thread) and 
two "***Exception: " being printed. If I interpret the core correctly, 
error is so well known to the compiler that it strips off the outer `error' 
in foo even without optimisations (which surprises me a bit).

With optimisations, ($) is inlined and `error $ error "bar"' is transformed 
to error (error "bar"), from then on both have identical structure and 
arrive at (mutatis mutandis) the same core (which is nearly the same as foo 
got without optimisations).

More information about the Haskell-Cafe mailing list