it seems like a rather aggressive optimisation strategy to me.

It is indeed aggressive.  Essentially when you declare a data type (like Step) to be fusible you are declaring that you never want a single value of type `Step` to be actually built at runtime -- they should all be fused away.   If a function returns value of type `Step`, it *can't* be fused away; so we have to inline the function.  Ditto if it takes a value of type `Step` as argument.

But in Harendra's applications it works rather well apparently.  

What is the goal you want to achieve.  "Fuse where possible" perhaps?   But what is "possible"?  

I have a feeling that in your library you do have a very clear idea of what should be fused and what should not. What is that idea?

Simon

On Mon, 8 Dec 2025 at 18:05, Jaro Reinders <jaro.reinders@gmail.com> wrote:
Dear Simon Peyton Jones and other interested GHC devs,

In a recent thread on the issue tracker of the vector package, I was asked for
my thoughts on a specific improvement to their fusion system [1]. I replied I
was a bit disillusioned with GHC's approach to fusion because it is not
reliable enough for the average user.

Simon pointed me to a GHC plugin by Harendra Kumar which acts as a lubricant
for fusion, telling GHC to inline more than usual [2].

I suggested we could integrate this into GHC by giving an inlining discount to
any function which contains function that is also mentioned in a rewrite rule.
However, I also noted I think this will have a large impact on code size and
compilation time in larger code bases. Simon agreed, but noted that Harendra's
plugin is much more selective than what I suggested.

Since I think this discussion is not that relevant to the particular issue in
the vector package, I'd like to continue this discussion here.

To figure out what Harendra's plugin does, I skimmed the blog post and found
this explanation [2]:

 > fusion-plugin gives programmers control through fusion annotations.
 >
 >     {-# ANN type Step Fuse #-}
 >     data Step s a = Yield a s | Skip s | Stop
 >
 > This tells the compiler:
 >
 > Any binding that scrutinizes or constructs Step must be inlined,
 > no matter its size.
 >
 > The plugin scans bindings during the simplifier pass:
 >
 > * If a fusible type (Step) is involved → mark it INLINE.
 > * Run another simplifier pass → constructors eliminated → fusion restored.

Translating this to the fold/build fusion system in GHC today, we would force
inlining of any function that calls `foldr` or `build` (or `augment`).
Essentially, we would want to inline any binding that refers to a function that
partakes in the rewrite rules for fusion. I'll admit we probably don't have to
generalise this to all rewrite rules, but even with this restriction it seems
like a rather aggressive optimisation strategy to me.

Please correct me if I have misinterpreted anything.

Cheers,

Jaro

[1] https://github.com/haskell/vector/issues/156#issuecomment-3623339336
[2] https://blog.composewell.com/versions/v2/posts/streamly-fusion-3.html