I have a function (*~)
. Most of the cost of evaluating x *~ y
comes from inspecting the second argument, roughly along these lines:
(*~) :: a -> b d -> c d a
x *~ y = case y' of
Bar -> cheapFunction y' x
Baz -> cheapFunction2 y' x
Quux -> cheapFunction3 y' x
where
y' = expensive y
Is there some way to convince GHC to partially evaluate operator sections like (*~ y)
?
I tried rewriting it like:
(*~) = flip go
where
go y = let y' = expensive y
in case y' of
Bar -> cheapFunction y'
Baz -> cheapFunction2 y'
Quux -> cheapFunction3 y'
but it didn't seem to help. I think this might be because flip
requires all it's arguments before it does the flipping?
One way would just be to flip the operator itself, but it reads much more naturally when the expensive operand is on the right hand side, because it lines up with an existing notation.
Can a properly crafted {-# RULE #-}
bail me out here? If so, what should it say? (I'm unclear on how far the sectioning syntax will have been desugared before rules look for matches, among other things.)
To trigger such an optimization you need to make sure that your function gets inlined. Place the {-# INLINE (*~) #-}
pragma before the declaration of the (*~)
function. I can't guarantee you that it'll solve your problem, but it's the only way I see it being approached. I'd examine the generated Core code with a tool like "ghc-core" afterwards to make sure.
However, your problem actually is just an indication of improper code composition. Your function is doing multiple unrelated things. expensive y
should simply be factored out of it, then your problem will be erased as such. I.e., the usage pattern should be x *~ expensive y
instead of x *~ y
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With