# Why add extra functions to type classes instead of using rewrite rules?

**URL:** <https://discourse.haskell.org/t/why-add-extra-functions-to-type-classes-instead-of-using-rewrite-rules/14820>\
**Category:** Uncategorized\
**Created:** [October 8, 2026, 4:35pm UTC](https://discourse.haskell.org/t/why-add-extra-functions-to-type-classes-instead-of-using-rewrite-rules/14820 "2026-10-08T16:35:49Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![jvanbruegge](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jvanbruegge/32/3970_2.png) [@jvanbruegge](https://discourse.haskell.org/u/jvanbruegge)\
**Post date:** [October 8, 2026, 4:35pm UTC](https://discourse.haskell.org/t/why-add-extra-functions-to-type-classes-instead-of-using-rewrite-rules/14820/1 "2026-10-08T16:35:49Z")

</div>

If we look e.g. at the definition of [Foldable](https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Data-Foldable.html#t:Foldable), aside from `foldMap`/`foldr`, it also contains a bunch of other methods like `minimum` that have a default implementation in terms of `foldMap`.

AFAIK this is done to allow to overwrite these default implementations in the instance declarations if there exists are more performant version for the concrete type.

Why would we clutter the definition of type classes just for this when rewrite rules would do the same? Is there a good reason?

E.g. `length` is part of the type class, so that e.g. Data.Map can just return the stored length instead of doing a traversal. But with a rewrite rule, the same could be achieved:  
`{-# RULES "Data.Foldable.length/Map" length = size #-}`

---

<div class="post-metadata">

**Author:** ![DavidB](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/davidb/32/3616_2.png) [@DavidB](https://discourse.haskell.org/u/DavidB)\
**Post date:** [October 8, 2026, 4:52pm UTC](https://discourse.haskell.org/t/why-add-extra-functions-to-type-classes-instead-of-using-rewrite-rules/14820/2 "2026-10-08T16:52:58Z")

</div>

Can you give a more detailed example of how this should work? As far as I can see:

```haskell
length :: Foldable t => t a -> Int
size :: Map k a -> Int

```

So I guess we would rather need a rule like this:

```haskell
{-# RULES "Data.Foldable.length/Map" length @Map = size #-}

```

I am not too familiar with what is allowed in `RULES`, @jaror might be more knowledgeable, but does GHC accept either of these rules? I am currently on a train and can’t check myself 🙂

---

<div class="post-metadata">

**Author:** ![prophet](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/prophet/32/5031_2.png) [@prophet](https://discourse.haskell.org/u/prophet)\
**Post date:** [October 8, 2026, 5:24pm UTC](https://discourse.haskell.org/t/why-add-extra-functions-to-type-classes-instead-of-using-rewrite-rules/14820/3 "2026-10-08T17:24:30Z")

</div>

If you did this, you would rely on rewrite rules firing for asymptotics and that would be a bit problematic.

E.g. in this case one immediate effect would be that any builds at `-O0` (including `ghci`) would use an abysmally slow linear time `length` implementation.

In optimized builds, rewrite rules still won’t work in polymorphic contexts that aren’t inlined or specialized. If you have

```hs
average :: Foldable f => f Double -> Double
average foldable = sum foldable / length foldable

```

then if `average` is not inlined or specialized, it will have to use the generic `length` implementation on `Foldable`s, whereas with a separate method, it will receive the optimized `length` implementation from the `Foldable` dictionary.

But even in optimized builds where every function you care about is entirely monomorphic, rewrite rules are still not guaranteed to fire depending on what exactly the optimizer does. If it inlines `length` before firing the rule, there is nothing for it to rewrite.

So even in this case there is still a chance that some length calls on maps will be extremely slow for no obvious reason.

---

<div class="post-metadata">

**Author:** ![augustss](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/augustss/32/887_2.png) [@augustss](https://discourse.haskell.org/u/augustss)\
**Post date:** [October 8, 2026, 5:51pm UTC](https://discourse.haskell.org/t/why-add-extra-functions-to-type-classes-instead-of-using-rewrite-rules/14820/4 "2026-10-08T17:51:01Z")

</div>

Because extra functions is Haskell. Rewrite rules are not (they are a GHCism).

---

<div class="post-metadata">

**Author:** ![jvanbruegge](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jvanbruegge/32/3970_2.png) [@jvanbruegge](https://discourse.haskell.org/u/jvanbruegge)\
**Post date:** [October 8, 2026, 5:58pm UTC](https://discourse.haskell.org/t/why-add-extra-functions-to-type-classes-instead-of-using-rewrite-rules/14820/5 "2026-10-08T17:58:52Z")

</div>

> [@DavidB](#):
>
> but does GHC accept either of these rules?

Yes, see [6.19.1. Rewrite rules — Glasgow Haskell Compiler 9.15.20260306 User's Guide](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/rewrite_rules.html#specialisation)

> [@prophet](#):
>
> it will receive the optimized `length` implementation from the `Foldable` dictionary.

That is indeed a good point that I did not think about

---

<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 8, 2026, 6:30pm UTC](https://discourse.haskell.org/t/why-add-extra-functions-to-type-classes-instead-of-using-rewrite-rules/14820/6 "2026-10-08T18:30:11Z")

</div>

I think the issue of how to handle unspecialized functions kills this idea (if only we had a JIT compiler…).

I think the issue with -O0 vs -O1 producing different asymptotics is not a huge issue in practice. There are already cases where -O0 is too slow to be practical. And we could conceivably change GHC to apply some lightweight rewrite rules even under -O0.

For optimized and monomorphic code you indeed still need to be careful defining rules on type class members, because GHC will indeed probably specialize or inline them before the rule can fire. But there are ways around it [as described in the GHC guide](https://downloads.haskell.org/~ghc/latest/docs/users_guide/exts/rewrite_rules.html#how-rules-interact-with-class-methods). Instead of defining the rule like this:

```haskell
{-# RULES "Data.Foldable.length/Map" length @Map = size #-}

```

You need to make sure that the `foldr` on your datatype has proper phase annotations:

```haskell
instance Foldable (Map k) where
   foldr = foldrMap

foldrMap = ...
{-# NOINLINE [1] foldrMap #-}

```

Then you can define the rewrite rule on that `foldrMap`:

```haskell
{-# RULES "Data.Foldable.length/Map" foldrMap (\_ n -> n + 1) 0 = size #-}

```

---

<div class="post-metadata">

**Author:** ![AntC2](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/antc2/32/1872_2.png) [@AntC2](https://discourse.haskell.org/u/AntC2)\
**Post date:** [October 9, 2026, 2:07am UTC](https://discourse.haskell.org/t/why-add-extra-functions-to-type-classes-instead-of-using-rewrite-rules/14820/7 "2026-10-09T02:07:44Z")

</div>

What @augustss said. GHC was already too complicated ten years ago. It’s bad enough keeping up with all the syntax it has introduced within Haskell; let alone a completely other bunch of syntax for `RULES`; and it seems subtleties about when/how they fire.
