Class op rules

Simon Peyton Jones simonpj at
Mon Mar 9 10:02:05 UTC 2020

But now that this has been brought up: currently, does adding an {-# INLINE op #-} (with or without phase) for a class op actually do anything?
Currently it does nothing, I think, and therefore should perhaps be rejected today.   So I hope no one is doing that.  Worth double checking:

  *   Hackage, to check that no one has INLINE on a method in a class decl
  *   GHC, to check that an INLINE on method in a class decl is ignored.
In contrast, an INLINE on an instance decl should mean that that particular class op instance method is inlined.   I hope this works correctly today.
So I personally could live with a situation where it is controllable for each classop individually - and I'd do the work to get that in.
OK.   This is actually user facing, so I think the right thing is to write a short GHC Proposal.  It needn’t take long to get approved.  But it does get more eyes on it.  And it provides a solid write up to refer to from the implementation.
You could draw on this thread for the raw material, so it would not be hard to write.

From: Christiaan Baaij <christiaan.baaij at>
Sent: 07 March 2020 08:53
To: Simon Peyton Jones <simonpj at>
Cc: Conal Elliott <conal at>; ghc-devs <ghc-devs at>
Subject: Re: Class op rules

Thanks for explaining Simon!

So I personally could live with a situation where it is controllable for each classop individually - and I'd do the work to get that in.
Where if the developer doesn't specify an INLINE pragma, it defaults to AlwaysActive.
That way, the change only affects users who have currently annotated their class op with an {-# INLINE[N] op #-}
(I would have to scour hackage to see if anyone has currently doing that... I hope not...)

But now that this has been brought up: currently, does adding an {-# INLINE op #-} (with or without phase) for a class op actually do anything?
Or is it basically superfluous because the class op already gets a BuiltInRule that's equal to INLINE AlwaysActive?
Or does it affect whether a default implementation for the method gets inlined into the dictionary?
If the latter, I guess we should use SPECIALIZE instead of INLINE for controlling the rule phase of the class op... unless that SPECIALIZE also has an effect on the class op default implementation...


On Sat, 7 Mar 2020 at 00:02, Simon Peyton Jones <simonpj at<mailto:simonpj at>> wrote:
Here’s how it works:

  *   The rewrite from    opi (D m1 … mn)   -->   mi

is done by a BuiltinRule: see MkId.mkDictSelId, and the BuiltinRule that is made there.

  *   At the moment, BuiltinRules are always active (in all phases), see GHC.Core.ruleActivation.  To allow them to be selectively active, we’d have to give them a ru_act fiels, like ordinary Rules.  That would not be hard.

  *   The phases go

     *   InitialPhase
     *   2
     *   1
     *   0

  *   We could make classop rules active only in phase 1 and 0, say.   I don’t know what the consequences would be; running the classop to pick a method out of a dictionary in turn reveals new function applications that might want to work in phase 2, say.

  *   Of course you can always add more phases, but that adds compile time.

  *   Would you want the classop phase to be fixed for every classop? Or controllable for each classop individually.  E.g.   class C a where {  op :: <bype>  {-# INLINE [2] op #-} }

Here the intent is that, since the pragmas is in the class decl, the pragma applies to the method selector.

I remember Conal raising this before, but I’ve forgotten the resolution.  I’m entirely open to changes here, if someone is willing to do the work, including checking for consequences.


From: ghc-devs <ghc-devs-bounces at<mailto:ghc-devs-bounces at>> On Behalf Of Conal Elliott
Sent: 06 March 2020 17:37
To: Christiaan Baaij <christiaan.baaij at<mailto:christiaan.baaij at>>
Cc: ghc-devs <ghc-devs at<mailto:ghc-devs at>>
Subject: Re: Class op rules

Thank you for raising this issue, Christiaan! The current policy (very early class-op inlining) is a major difficulty and the main source of fragility in my compiling-to-categories implementation. I have a tediously programmed and delicately balanced collection of techniques to intercept and transform class ops to non-ops early and then transform back late for elimination, but it doesn't work in all situations. Since class operations roughly correspond to operations in various algebraic abstractions---interfaces with laws---I often want to exploit exactly those laws as rewrite rules, and yet those rules currently cannot be used dependably.  - Conal

On Fri, Mar 6, 2020 at 7:22 AM Christiaan Baaij <christiaan.baaij at<mailto:christiaan.baaij at>> wrote:

The other day I was experimenting with RULES and got this warning:

src/Clash/Sized/Vector.hs:2159:11: warning: [-Winline-rule-shadowing]
    Rule "map Pack" may never fire
      because rule "Class op pack" for ‘pack’ might fire first
    Probable fix: add phase [n] or [~n] to the competing rule
2159 | {-# RULES "map Pack" map pack = id #-}

The warning seems to suggests two things:
1. "Class op" -> "dictionary projection" are implemented as rewrite rules and executed the same way as other user-defined RULES
2. These rules run first, and you cannot run anything before them

Now my question is, is 1. actually true? or is that warning just a (white) lie?
If 1. is actually true, would there be any objections to adding a "-1" phase: where RULES specified to start from phase "-1" onward fire before any of the Class op rules.
I'm quite willing to implement the above if A) Class op rules are actually implemented as builtin RULES; B) there a no objections to this "-1" phase.


ghc-devs mailing list
ghc-devs at<mailto:ghc-devs at><>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>

More information about the ghc-devs mailing list