7.9. Rewrite rules

The programmer can specify rewrite rules as part of the source program (in a pragma). GHC applies these rewrite rules wherever it can, provided (a) the -O flag (Section 4.9) is on, and (b) the -frules-off flag (Section 4.9.2) is not specified.

Here is an example:
  {-# RULES
        "map/map"       forall f g xs. map f (map g xs) = map (f.g) xs
  #-}

7.9.1. Syntax

From a syntactic point of view:

7.9.2. Semantics

From a semantic point of view:

7.9.3. List fusion

The RULES mechanism is used to implement fusion (deforestation) of common list functions. If a "good consumer" consumes an intermediate list constructed by a "good producer", the intermediate list should be eliminated entirely.

The following are good producers:

The following are good consumers:

So, for example, the following should generate no intermediate lists:
array (1,10) [(i,i*i) | i <- map (+ 1) [0..9]]

This list could readily be extended; if there are Prelude functions that you use a lot which are not included, please tell us.

If you want to write your own good consumers or producers, look at the Prelude definitions of the above functions to see how to do so.

7.9.4. Specialisation

Rewrite rules can be used to get the same effect as a feature present in earlier versions of GHC. For example, suppose that:
genericLookup :: Ord a => Table a b   -> a   -> b
intLookup     ::          Table Int b -> Int -> b
where intLookup is an implementation of genericLookup that works very fast for keys of type Int. You might wish to tell GHC to use intLookup instead of genericLookup whenever the latter was called with type Table Int b -> Int -> b. It used to be possible to write
{-# SPECIALIZE genericLookup :: Table Int b -> Int -> b = intLookup #-}
This feature is no longer in GHC, but rewrite rules let you do the same thing:
{-# RULES "genericLookup/Int" genericLookup = intLookup #-}
This slightly odd-looking rule instructs GHC to replace genericLookup by intLookup whenever the types match. What is more, this rule does not need to be in the same file as genericLookup, unlike the SPECIALIZE pragmas which currently do (so that they have an original definition available to specialise).

It is Your Responsibility to make sure that intLookup really behaves as a specialised version of genericLookup!!!

An example in which using RULES for specialisation will Win Big:
toDouble :: Real a => a -> Double
toDouble = fromRational . toRational

{-# RULES "toDouble/Int" toDouble = i2d #-}
i2d (I# i) = D# (int2Double# i) -- uses Glasgow prim-op directly
The i2d function is virtually one machine instruction; the default conversion—via an intermediate Rational—is obscenely expensive by comparison.

7.9.5. Controlling what's going on

7.9.6. CORE pragma

The external core format supports "Note" annotations; the CORE pragma gives a way to specify what these should be in your Haskell source code. Syntactically, core annotations are attached to expressions and take a Haskell string literal as an argument. The following function definition shows an example:
f x = ({-# CORE "foo" #-} show) ({-# CORE "bar" #-} x)
Sematically, this is equivalent to:
g x = show x

However, when external for is generated (via -fext-core), there will be Notes attached to the expressions show and x. The core function declaration for f is:

  f :: %forall a . GHCziShow.ZCTShow a ->
                   a -> GHCziBase.ZMZN GHCziBase.Char =
    \ @ a (zddShow::GHCziShow.ZCTShow a) (eta::a) ->
        (%note "foo"
         %case zddShow %of (tpl::GHCziShow.ZCTShow a)
           {GHCziShow.ZCDShow
            (tpl1::GHCziBase.Int ->
                   a ->
                   GHCziBase.ZMZN GHCziBase.Char -> GHCziBase.ZMZN GHCziBase.Cha
r)
            (tpl2::a -> GHCziBase.ZMZN GHCziBase.Char)
            (tpl3::GHCziBase.ZMZN a ->
                   GHCziBase.ZMZN GHCziBase.Char -> GHCziBase.ZMZN GHCziBase.Cha
r) ->
              tpl2})
        (%note "foo"
         eta);

Here, we can see that the function show (which has been expanded out to a case expression over the Show dictionary) has a %note attached to it, as does the expression eta (which used to be called x).