diff options
Diffstat (limited to 'docs/users_guide/exts/rewrite_rules.rst')
-rw-r--r-- | docs/users_guide/exts/rewrite_rules.rst | 471 |
1 files changed, 471 insertions, 0 deletions
diff --git a/docs/users_guide/exts/rewrite_rules.rst b/docs/users_guide/exts/rewrite_rules.rst new file mode 100644 index 0000000000..b384faeb9e --- /dev/null +++ b/docs/users_guide/exts/rewrite_rules.rst @@ -0,0 +1,471 @@ +.. _rewrite-rules: + +Rewrite rules +============= + +.. index:: + single: rewrite rules + +.. pragma:: RULES "⟨name⟩" forall ⟨binder⟩ ... . ⟨expr⟩ = ⟨expr⟩ ... + + :where: top-level + + Define a rewrite rule to be used to optimize a source program. + +The programmer can specify rewrite rules as part of the source program +(in a pragma). Here is an example: :: + + {-# RULES + "map/map" forall f g xs. map f (map g xs) = map (f.g) xs + #-} + +Use the debug flag :ghc-flag:`-ddump-simpl-stats` to see what rules fired. If +you need more information, then :ghc-flag:`-ddump-rule-firings` shows you each +individual rule firing and :ghc-flag:`-ddump-rule-rewrites` also shows what the +code looks like before and after the rewrite. + +.. ghc-flag:: -fenable-rewrite-rules + :shortdesc: Switch on all rewrite rules (including rules generated by + automatic specialisation of overloaded functions). Implied by + :ghc-flag:`-O`. + :type: dynamic + :reverse: -fno-enable-rewrite-rules + :category: optimization + + Allow the compiler to apply rewrite rules to the source program. + +Syntax +------ + +From a syntactic point of view: + +- There may be zero or more rules in a :pragma:`RULES` pragma, separated by + semicolons (which may be generated by the layout rule). + +- The layout rule applies in a pragma. Currently no new indentation + level is set, so if you put several rules in single ``RULES`` pragma and + wish to use layout to separate them, you must lay out the starting in + the same column as the enclosing definitions. :: + + {-# RULES + "map/map" forall f g xs. map f (map g xs) = map (f.g) xs + "map/append" forall f xs ys. map f (xs ++ ys) = map f xs ++ map f ys + #-} + + Furthermore, the closing ``#-}`` should start in a column to the + right of the opening ``{-#``. + +- Each rule has a name, enclosed in double quotes. The name itself has + no significance at all. It is only used when reporting how many times + the rule fired. + +- A rule may optionally have a phase-control number (see + :ref:`phase-control`), immediately after the name of the rule. Thus: :: + + {-# RULES + "map/map" [2] forall f g xs. map f (map g xs) = map (f.g) xs + #-} + + The ``[2]`` means that the rule is active in Phase 2 and subsequent + phases. The inverse notation ``[~2]`` is also accepted, meaning that + the rule is active up to, but not including, Phase 2. + + Rules support the special phase-control notation ``[~]``, which means + the rule is never active. This feature supports plugins (see + :ref:`compiler-plugins`), by making it possible to define a RULE that + is never run by GHC, but is nevertheless parsed, typechecked etc, so + that it is available to the plugin. + +- Each (term) variable mentioned in a rule must either be in scope (e.g. + ``map``), or bound by the ``forall`` (e.g. ``f``, ``g``, ``xs``). The + variables bound by the ``forall`` are called the *pattern* variables. + They are separated by spaces, just like in a type ``forall``. + +- A pattern variable may optionally have a type signature. If the type + of the pattern variable is polymorphic, it *must* have a type + signature. For example, here is the ``foldr/build`` rule: :: + + "fold/build" forall k z (g::forall b. (a->b->b) -> b -> b) . + foldr k z (build g) = g k z + + Since ``g`` has a polymorphic type, it must have a type signature. + +- If :extension:`ExplicitForAll` is enabled, type/kind variables can also be + explicitly bound. For example: :: + + {-# RULES "id" forall a. forall (x :: a). id @a x = x #-} + + When a type-level explicit ``forall`` is present, each type/kind variable + mentioned must now also be either in scope or bound by the ``forall``. In + particular, unlike some other places in Haskell, this means free kind + variables will not be implicitly bound. For example: :: + + "this_is_bad" forall (c :: k). forall (x :: Proxy c) ... + "this_is_ok" forall k (c :: k). forall (x :: Proxy c) ... + + When bound type/kind variables are needed, both foralls must always be + included, though if no pattern variables are needed, the second can be left + empty. For example: :: + + {-# RULES "map/id" forall a. forall. map (id @a) = id @[a] #-} + +- The left hand side of a rule must consist of a top-level variable + applied to arbitrary expressions. For example, this is *not* OK: :: + + "wrong1" forall e1 e2. case True of { True -> e1; False -> e2 } = e1 + "wrong2" forall f. f True = True + "wrong3" forall x. Just x = Nothing + + In ``"wrong1"``, the LHS is not an application; in ``"wrong2"``, the + LHS has a pattern variable in the head. In ``"wrong3"``, the LHS consists + of a *constructor*, rather than a *variable*, applied to an argument. + +- A rule does not need to be in the same module as (any of) the + variables it mentions, though of course they need to be in scope. + +- All rules are implicitly exported from the module, and are therefore + in force in any module that imports the module that defined the rule, + directly or indirectly. (That is, if A imports B, which imports C, + then C's rules are in force when compiling A.) The situation is very + similar to that for instance declarations. + +- Inside a :pragma:`RULES` "``forall``" is treated as a keyword, regardless of any + other flag settings. Furthermore, inside a :pragma:`RULES`, the language + extension :extension:`ScopedTypeVariables` is automatically enabled; see + :ref:`scoped-type-variables`. + +- Like other pragmas, :pragma:`RULES` pragmas are always checked for scope errors, + and are typechecked. Typechecking means that the LHS and RHS of a + rule are typechecked, and must have the same type. However, rules are + only *enabled* if the :ghc-flag:`-fenable-rewrite-rules` flag is on (see + :ref:`rule-semantics`). + +.. _rule-semantics: + +Semantics +--------- + +From a semantic point of view: + +- Rules are enabled (that is, used during optimisation) by the + :ghc-flag:`-fenable-rewrite-rules` flag. This flag is implied by + :ghc-flag:`-O`, and may be switched off (as usual) by + :ghc-flag:`-fno-enable-rewrite-rules <-fenable-rewrite-rules>`. (NB: enabling + :ghc-flag:`-fenable-rewrite-rules` without :ghc-flag:`-O` may not do what you + expect, though, because without :ghc-flag:`-O` GHC ignores all optimisation + information in interface files; see :ghc-flag:`-fignore-interface-pragmas`). + Note that :ghc-flag:`-fenable-rewrite-rules` is an + *optimisation* flag, and has no effect on parsing or typechecking. + +- Rules are regarded as left-to-right rewrite rules. When GHC finds an + expression that is a substitution instance of the LHS of a rule, it + replaces the expression by the (appropriately-substituted) RHS. By "a + substitution instance" we mean that the LHS can be made equal to the + expression by substituting for the pattern variables. + +- GHC makes absolutely no attempt to verify that the LHS and RHS of a + rule have the same meaning. That is undecidable in general, and + infeasible in most interesting cases. The responsibility is entirely + the programmer's! + +- GHC makes no attempt to make sure that the rules are confluent or + terminating. For example: :: + + "loop" forall x y. f x y = f y x + + This rule will cause the compiler to go into an infinite loop. + +- If more than one rule matches a call, GHC will choose one arbitrarily + to apply. + +- GHC currently uses a very simple, syntactic, matching algorithm for + matching a rule LHS with an expression. It seeks a substitution which + makes the LHS and expression syntactically equal modulo alpha + conversion. The pattern (rule), but not the expression, is + eta-expanded if necessary. (Eta-expanding the expression can lead to + laziness bugs.) But not beta conversion (that's called higher-order + matching). + + Matching is carried out on GHC's intermediate language, which + includes type abstractions and applications. So a rule only matches + if the types match too. See :ref:`rule-spec` below. + +- GHC keeps trying to apply the rules as it optimises the program. For + example, consider: :: + + let s = map f + t = map g + in + s (t xs) + + The expression ``s (t xs)`` does not match the rule ``"map/map"``, + but GHC will substitute for ``s`` and ``t``, giving an expression + which does match. If ``s`` or ``t`` was (a) used more than once, and + (b) large or a redex, then it would not be substituted, and the rule + would not fire. + +.. _rules-inline: + +How rules interact with ``INLINE``/``NOINLINE`` pragmas +------------------------------------------------------- + +Ordinary inlining happens at the same time as rule rewriting, which may +lead to unexpected results. Consider this (artificial) example :: + + f x = x + g y = f y + h z = g True + + {-# RULES "f" f True = False #-} + +Since ``f``\'s right-hand side is small, it is inlined into ``g``, to +give :: + + g y = y + +Now ``g`` is inlined into ``h``, but ``f``\'s RULE has no chance to +fire. If instead GHC had first inlined ``g`` into ``h`` then there would have +been a better chance that ``f``\'s :pragma:`RULES` might fire. + +The way to get predictable behaviour is to use a :pragma:`NOINLINE` pragma, or an +``INLINE[⟨phase⟩]`` pragma, on ``f``, to ensure that it is not inlined until +its :pragma:`RULES` have had a chance to fire. The warning flag +:ghc-flag:`-Winline-rule-shadowing` (see :ref:`options-sanity`) warns about +this situation. + +.. _conlike: + +How rules interact with ``CONLIKE`` pragmas +------------------------------------------- + +GHC is very cautious about duplicating work. For example, consider :: + + f k z xs = let xs = build g + in ...(foldr k z xs)...sum xs... + {-# RULES "foldr/build" forall k z g. foldr k z (build g) = g k z #-} + +Since ``xs`` is used twice, GHC does not fire the foldr/build rule. +Rightly so, because it might take a lot of work to compute ``xs``, which +would be duplicated if the rule fired. + +Sometimes, however, this approach is over-cautious, and we *do* want the +rule to fire, even though doing so would duplicate redex. There is no +way that GHC can work out when this is a good idea, so we provide the +``CONLIKE`` pragma to declare it, thus: :: + + {-# INLINE CONLIKE [1] f #-} + f x = blah + +``CONLIKE`` is a modifier to an ``INLINE`` or ``NOINLINE`` pragma. It specifies that +an application of ``f`` to one argument (in general, the number of arguments +to the left of the ``=`` sign) should be considered cheap enough to +duplicate, if such a duplication would make rule fire. (The name +"CONLIKE" is short for "constructor-like", because constructors +certainly have such a property.) The :pragma:`CONLIKE` pragma is a modifier to +:pragma:`INLINE`/:pragma:`NOINLINE` because it really only makes sense to match +``f`` on the LHS of a rule if you are sure that ``f`` is not going to be inlined +before the rule has a chance to fire. + +.. _rules-class-methods: + +How rules interact with class methods +------------------------------------- + +Giving a RULE for a class method is a bad idea: :: + + class C a where + op :: a -> a -> a + + instance C Bool where + op x y = ...rhs for op at Bool... + + {-# RULES "f" op True y = False #-} + +In this example, ``op`` is not an ordinary top-level function; it is a +class method. GHC rapidly rewrites any occurrences of +``op``\-used-at-type-Bool to a specialised function, say ``opBool``, +where :: + + opBool :: Bool -> Bool -> Bool + opBool x y = ..rhs for op at Bool... + +So the RULE never has a chance to fire, for just the same reasons as in +:ref:`rules-inline`. + +The solution is to define the instance-specific function yourself, with +a pragma to prevent it being inlined too early, and give a RULE for it: :: + + instance C Bool where + op = opBool + + opBool :: Bool -> Bool -> Bool + {-# NOINLINE [1] opBool #-} + opBool x y = ..rhs for op at Bool... + + {-# RULES "f" opBool True y = False #-} + +If you want a RULE that truly applies to the overloaded class method, +the only way to do it is like this: :: + + class C a where + op_c :: a -> a -> a + + op :: C a => a -> a -> a + {-# NOINLINE [1] op #-} + op = op_c + + {-# RULES "reassociate" op (op x y) z = op x (op y z) #-} + +Now the inlining of ``op`` is delayed until the rule has a chance to +fire. The down-side is that instance declarations must define ``op_c``, +but all other uses should go via ``op``. + +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: + +- List comprehensions + +- Enumerations of ``Int``, ``Integer`` and ``Char`` (e.g. + ``['a'..'z']``). + +- Explicit lists (e.g. ``[True, False]``) + +- The cons constructor (e.g ``3:4:[]``) + +- ``++`` + +- ``map`` + +- ``take``, ``filter`` + +- ``iterate``, ``repeat`` + +- ``zip``, ``zipWith`` + +The following are good consumers: + +- List comprehensions + +- ``array`` (on its second argument) + +- ``++`` (on its first argument) + +- ``foldr`` + +- ``map`` + +- ``take``, ``filter`` + +- ``concat`` + +- ``unzip``, ``unzip2``, ``unzip3``, ``unzip4`` + +- ``zip``, ``zipWith`` (but on one argument only; if both are good + producers, ``zip`` will fuse with one but not the other) + +- ``partition`` + +- ``head`` + +- ``and``, ``or``, ``any``, ``all`` + +- ``sequence_`` + +- ``msum`` + +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. + +.. _rule-spec: + +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. + +.. _controlling-rules: + +Controlling what's going on in rewrite rules +-------------------------------------------- + +- Use :ghc-flag:`-ddump-rules` to see the rules that are defined *in this + module*. This includes rules generated by the specialisation pass, + but excludes rules imported from other modules. + +- Use :ghc-flag:`-ddump-simpl-stats` to see what rules are being fired. If you + add :ghc-flag:`-dppr-debug` you get a more detailed listing. + +- Use :ghc-flag:`-ddump-rule-firings` or :ghc-flag:`-ddump-rule-rewrites` to see in + great detail what rules are being fired. If you add :ghc-flag:`-dppr-debug` + you get a still more detailed listing. + +- The definition of (say) ``build`` in ``GHC/Base.hs`` looks like + this: :: + + build :: forall a. (forall b. (a -> b -> b) -> b -> b) -> [a] + {-# INLINE build #-} + build g = g (:) [] + + Notice the :pragma:`INLINE`! That prevents ``(:)`` from being inlined when + compiling ``PrelBase``, so that an importing module will “see” the + ``(:)``, and can match it on the LHS of a rule. ``INLINE`` prevents + any inlining happening in the RHS of the ``INLINE`` thing. I regret + the delicacy of this. + +- In ``libraries/base/GHC/Base.hs`` look at the rules for ``map`` to + see how to write rules that will do fusion and yet give an efficient + program even if fusion doesn't happen. More rules in + ``GHC/List.hs``. + |