I think parametricity implies it can never help to specialize a free type variable (i.e. one with no class constraints), like a function :: Maybe a -> Bool
. Are there any cases in the context of ghc where this might actually help that I'm missing?
(Partial answer, but I'd like to share anyway.)
I tried to specialize a GADT-eliminating function, to see how GHC handles it. Why? Well, it sounded as a crazy way to get around parametricity with only unconstrained variables, and I was curious.
data T a where
A :: Int -> T Int
B :: String -> T String
{-# SPECIALIZE foo :: T Int -> Int #-}
foo :: T a -> Int
foo (A i) = i
foo (B s) = length s
The compiler slightly complained about this:
warning: SPECIALISE pragma for non-overloaded function `foo'
|
| {-# SPECIALIZE foo :: T Int -> Int #-}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Still, the generated Core showed what I hoped to see: the specialized form.
foo :: forall a. T a -> Int
foo
= \ (@a_aPp) (ds_dZc :: T a_aPp) ->
case ds_dZc of {
A co_aPq [Dmd=A] i_auj -> i_auj;
B co_aPs [Dmd=A] s_auk ->
case GHC.List.$wlenAcc @Char s_auk 0# of ww2_a1Ld { __DEFAULT ->
GHC.Types.I# ww2_a1Ld
}
}
Main.foo_$sfoo :: T Int -> Int
Main.foo_$sfoo
= \ (ds_dZc :: T Int) ->
case ds_dZc of { A co_aPq [Dmd=A] i_auj -> i_auj }
------ Local rules for imported ids --------
"SPEC foo" forall. foo @Int = Main.foo_$sfoo
Is this useful? I don't know. Is this a true optimization? Maybe. (Perhaps we should look at the assembly to answer that.)
Still, some rewriting rule is being generated pointing to simplified code, so this might potentially turn trigger some optimization in a more serious program. Hard to tell with only the above evidence, but I wouldn't dismiss the possibility.
I'd answer "can the specialization of an unconstrained type variable be of any use, at least in some specific contexts?" with "probably".