# How Cool is Rulz!

**URL:** <https://discourse.haskell.org/t/how-cool-is-rulz/7738>\
**Category:** Learn\
**Created:** [October 1, 2023, 2:52am UTC](https://discourse.haskell.org/t/how-cool-is-rulz/7738 "2023-10-01T02:52:14Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![waivio](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/waivio/32/1354_2.png) [@waivio](https://discourse.haskell.org/u/waivio)\
**Post date:** [October 1, 2023, 2:52am UTC](https://discourse.haskell.org/t/how-cool-is-rulz/7738/1 "2023-10-01T02:52:14Z")

</div>

I’m trying to learn Rewrite Rules. It looks like GHC-9.8.1 might have some Rewrite Rule improvements.

Say for instance that I have a library, that exports a class called `FusedOps`, there could be a function called `fma` with the type of:

`fma :: FusedOps a => a -> a -> a -> a`

And perhaps there’s good reason to believe that `fma` is Fused Multiply Add: (a \* b) + c, such that only one rounding operation occurs.

How would you write the rule?

{-# RULES  
“FusedOps/fma” forall a b c. FusedOps (a, b, c) =\> (a \* b) + c = fma a b c  
#-}

---

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [October 1, 2023, 9:32am UTC](https://discourse.haskell.org/t/how-cool-is-rulz/7738/2 "2023-10-01T09:32:28Z")

</div>

As far as I know it is currently not possible to make a rewrite rule depend on whether the types are instances of a certain type class. You’d have to write separate rules for all your types:

```haskell
{-# RULES
  "FusedOps/fma/Double" forall (a :: Double) (b :: Double) (c :: Double). (a * b) + c = fma a b c
  "FusedOps/fma/Int" forall (a :: Int) (b :: Int) (c :: Int). (a * b) + c = fma a b c
  #-}

```

But even those rules generate warnings like this:

```haskell
T.hs:8:1: warning: [-Winline-rule-shadowing]
    Rule "FusedOps/fma/Double" may never fire
      because rule "Class op *" for ‘*’ might fire first
    Probable fix: add phase [n] or [~n] to the competing rule
  |
8 | "FusedOps/fma/Double" forall (a :: Double) (b :: Double) (c :: Double). (a * b) + c = fma a b c
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

```

I think this has to do with what is discussed in this section in the documentation: [6.19.1. Rewrite rules — Glasgow Haskell Compiler 9.6.3 User's Guide](https://downloads.haskell.org/ghc/9.6.3/docs/users_guide/exts/rewrite_rules.html#how-rules-interact-with-class-methods).

So you can write a rule like that for your own types or for newtypes around existing types, e.g.:

```haskell
instance Num MyDouble where
  x + y = plusMyDouble x y
  x * y = timesMyDouble x y

plusMyDouble :: MyDouble -> MyDouble -> MyDouble
{-# NOINLINE [1] plusMyDouble #-}
plusMyDouble (MyDouble x) (MyDouble y) = MyDouble (x + y)

timesMyDouble :: MyDouble -> MyDouble -> MyDouble
{-# NOINLINE [1] timesMyDouble #-}
timesMyDouble (MyDouble x) (MyDouble y) = MyDouble (x * y)

instance FusedOps MyDouble

{-# RULES
"FusedOps/fma/MyDouble" forall a b c. plusMyDouble (timesMyDouble a b) c = fma a b c
   #-}

```

But now it does not make that much sense to use a separate class for `FusedOps` at all. You can just as well write it as a custom function:

```haskell
fmaMyDouble :: MyDouble -> MyDouble -> MyDouble -> MyDouble
fmaMyDouble = ...

{-# RULES
"fma/MyDouble" forall a b c. plusMyDouble (timesMyDouble a b) c = fmaMyDouble a b c
   #-}

```

Or of course just write out the definition in the rewrite rule.

---

<div class="post-metadata">

**Author:** ![waivio](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/waivio/32/1354_2.png) [@waivio](https://discourse.haskell.org/u/waivio)\
**Post date:** [October 1, 2023, 8:03pm UTC](https://discourse.haskell.org/t/how-cool-is-rulz/7738/3 "2023-10-01T20:03:41Z")

</div>

Oh, okay, so the `Num` class functions disappear before the rules have a chance to fire.  
Thanks for describing the path forward, I will try and pursue that more.

I have two more questions:

1. Where can I find the “Class op \*” rule? I searched the GHC gitlab and the result didn’t popup in any code, but there were several issues associated with it.
2. For the associative nature of the arithmetic would I have to make two rules, one for `(a * b) + c` and the other for `c + (a * b)`?

Thanks,

---

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [October 1, 2023, 8:31pm UTC](https://discourse.haskell.org/t/how-cool-is-rulz/7738/4 "2023-10-01T20:31:30Z")

</div>

> [@waivio](#):
>
> Where can I find the “Class op \*” rule?

The class op rule is built into the compiler. [Here’s the source code:](https://gitlab.haskell.org/ghc/ghc/-/blob/c1a3ecde720b3bddc2c8616daaa06ee324e602ab/compiler/GHC/Types/Id/Make.hs#L520-526)

```haskell
    -- This is the built-in rule that goes
    -- op (dfT d1 d2) ---> opT d1 d2
    rule = BuiltinRule { ru_name = fsLit "Class op " `appendFS`
                                     occNameFS (getOccName name)
                       , ru_fn = name
                       , ru_nargs = n_ty_args + 1
                       , ru_try = dictSelRule val_index n_ty_args }

```

So essentially it rewrites polymorphic calls to monomorphic calls if the concrete type (or more accurately the class dictionary) is known.

> [@waivio](#):
>
> For the associative nature of the arithmetic would I have to make two rules, one for `(a * b) + c` and the other for `c + (a * b)`?

Yes, I believe GHC doesn’t know about associativity and commutativity at this level.

---

<div class="post-metadata">

**Author:** ![waivio](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/waivio/32/1354_2.png) [@waivio](https://discourse.haskell.org/u/waivio)\
**Post date:** [October 27, 2023, 2:30am UTC](https://discourse.haskell.org/t/how-cool-is-rulz/7738/6 "2023-10-27T02:30:43Z")

</div>

Thanks for the help and the learning experience.

From what I found is that GHC likes a simpler forall signature, rather than one with types annotations for instance: `fma` fused multiply add: (a \* b) + c

`"posit/fma" forall a b c. positADD (positMULT a b) c = fma a b c`

This seams to work from GHC 8.10.7 until 9.4.7, but then the rewrite rule fails without warning with GHC 9.6.3!

I searched gitlab about `rewrite rules` and `ghc-9.6.3`, but couldn’t find anything. Are there any known issues?

Thanks,

---

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [October 27, 2023, 8:32am UTC](https://discourse.haskell.org/t/how-cool-is-rulz/7738/7 "2023-10-27T08:32:48Z")

</div>

I don’t know of any issues. Can you show your code?

---

<div class="post-metadata">

**Author:** ![waivio](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/waivio/32/1354_2.png) [@waivio](https://discourse.haskell.org/u/waivio)\
**Post date:** [October 27, 2023, 4:08pm UTC](https://discourse.haskell.org/t/how-cool-is-rulz/7738/8 "2023-10-27T16:08:27Z")

</div>

Here is the branch in github:

> **[GitHub - waivio/posit at fusedOps-rewrite-rules](https://github.com/waivio/posit/tree/fusedOps-rewrite-rules)**
>
> fusedOps-rewrite-rules

To test the fused rewrite rules I use this stack command:  
`stack test posit:test-posit-fusedRewrite --flag posit:do-rewrite`

When set to one of the stackage lts’s like `lts-21.13`, it will print the rules that fire like: `Rule fired: posit/fsmSub (Posit)` buried in with all of the other rules, but when set to `nightly-2023-10-16` for `ghc-9.6.3`, I don’t see any of my rules fire.

---

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [October 27, 2023, 6:55pm UTC](https://discourse.haskell.org/t/how-cool-is-rulz/7738/9 "2023-10-27T18:55:34Z")

</div>

I think I can tell what’s going on, but I don’t know why this happens. In the Core I found this dictionary for `Num (Posit es)`:

```haskell
Posit.$fNumPosit
  = \ (@(es :: ES)) ($dPositC :: PositC es) ->
      GHC.Num.C:Num
        @(Posit es)
        (Posit.$fNumPosit_$c+ @es $dPositC)
        (Posit.$fNumPosit_$c- @es $dPositC)
        (Posit.$fNumPosit_$c* @es $dPositC)
        (positNEG @es $dPositC)
        (positABS @es $dPositC)
        (positSIGNUM @es $dPositC)
        (Posit.$fNumPosit_$cfromInteger @es $dPositC)

```

I expected to see `positADD` there instead of `Posit.$fNumPosit_$c+`. For some reason GHC creates an intermediate function here that seems to do nothing interesting:

```haskell
-- RHS size: {terms: 4, types: 4, coercions: 0, joins: 0/0}
Posit.$fNumPosit_$c+ [Occ=LoopBreaker]
  :: forall (es :: ES). PositC es => Posit es -> Posit es -> Posit es
[GblId,
 Arity=3,
 Str=<SP(L,L,SC(S,L),L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L)><1P(A,1L)><MP(A,L)>,
 Cpr=1,
 Unf=OtherCon []]
Posit.$fNumPosit_$c+
  = \ (@(es :: ES)) ($dPositC :: PositC es) -> positADD @es $dPositC
end Rec }

```

The rewrite rule won’t apply if this function is used instead of `positADD` directly.

@sgraf is probably better equipped to explain why this happens.

Here’s the Core of `positADD`:

```haskell
-- RHS size: {terms: 11, types: 14, coercions: 0, joins: 0/0}
positADD [InlPrag=NOINLINE[final]]
  :: forall (es :: ES). PositC es => Posit es -> Posit es -> Posit es
[GblId,
 Arity=3,
 Str=<SP(L,L,SC(S,L),L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L,L)><1P(A,1L)><MP(A,L)>,
 Cpr=1,
 Unf=Unf{Src=StableSystem, TopLvl=True,
         Value=True, ConLike=True, WorkFree=True, Expandable=True,
         Guidance=ALWAYS_IF(arity=3,unsat_ok=True,boring_ok=False)
         Tmpl= \ (@(es :: ES))
                 ($dPositC [Occ=Once1] :: PositC es)
                 (x [Occ=Once1!] :: Posit es)
                 (y [Occ=Once1] :: Posit es) ->
                 case x of { Posit _ [Occ=Dead] ww1 [Occ=Once1] ->
                 Posit.$wpositADD @es $dPositC ww1 y
                 }}]
positADD
  = \ (@(es :: ES))
      ($dPositC :: PositC es)
      (x :: Posit es)
      (y :: Posit es) ->
      case x of { Posit ww ww1 -> Posit.$wpositADD @es $dPositC ww1 y }

```

---

<div class="post-metadata">

**Author:** ![waivio](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/waivio/32/1354_2.png) [@waivio](https://discourse.haskell.org/u/waivio)\
**Post date:** [November 6, 2023, 4:49am UTC](https://discourse.haskell.org/t/how-cool-is-rulz/7738/10 "2023-11-06T04:49:37Z")

</div>

Hmm…  
This is unfortunate.

So is this unique to my specific use case? It seems like this would be something that would effect a lot of libraries attempting to use rewrite rules.
