GHC fails to specialize higher-rank arguments even if dictionaries are monomorphic
Consider the following program:
{-# LANGUAGE AllowAmbiguousTypes, RankNTypes, ScopedTypeVariables, TypeApplications #-}
showBang :: Show a => a -> String
showBang x = show x ++ "!"
class C a where
f :: (Show a => r) -> r
instance C () where
f x = x
g :: forall a. C a => a -> String
g x = f @a (showBang x)
{-# INLINABLE[0] g #-}
s :: String
s = g ()
When the specializer looks at this program, it will discover the call g ()
, and it will specialize g @()
, as it should. However, it will not specialize showBang @()
, even though showBang
is used completely monomorphically in the body of the specialized version of g
! Why does this happen? Well, after desugaring, g
becomes something like
g = \@a $dC_a x ->
f @a $dC_a @String (\$dShow_a -> showBang @a $dShow_a x)
so when the specializer goes to look at the call site of showBang
, it simply sees showBang @() $dShow_a x
. Since $dShow_a
is lambda-bound, GHC considers it to be “uninteresting,” and it declines to specialize showBang
. However, this is a bad decision, as this use of showBang
is trivially monomorphic!
This is really biting me, as I am using a construction like the above in a scenario where specializing the monomorphic call site opens up an enormous number of additional specialization opportunities, and I am relying on GHC to perform that specialization to get reasonable performance.
Environment
- GHC version used: 8.9.0.20191024