Skip to content

Missed specialisation opportunities

I'm going to document one way (the only way I think) in which we can miss specialisation opportunities via class methods.

Background

g :: Ord a => blahg
g = /\ a \(d::Ord a). ...

f :: Ord a => blahf
f = /\ a \(d::Ord a). ...(g a d)...

....f @Int dOrdInt...

From the call f @Int dOrdInt we specialise f to get

$sf :: blahf[Int/a]
$sf = ...(g Int dOrdInt)...

Now, the specialiser sees this specialised call to g, arising from the specialised RHS of $sf, and hence specialises g as well.

The lost opportunity

Suppose now that we had

class C a where
  foo :: forall b. Ord b => blah
  ...

fooInt :: forall b. Ord b => blah[Int/a]
fooInt = ...

-- instance C Int where
--   foo = fooInt

dCInt :: CInt
dCInt = MkC fooInt ...

f :: C a => blahf
f = /\ a \(d::C a). ...(foo a d)...

...f @Int dCInt @Char dOrdChar...

Notice that foo has local, overloaded polymorphism, so that

   foo :: forall a. C a => forall b. Ord b => blah

That is what gives rise to the lost opportunity.

We specialise f as before:

$sf :: blah[Int/a]
$sf = ...(foo @Int dCInt @Char dOrdChar)...

But now we don't currently go any further. Yet later, when the simplifer runs, we'll run the built-in method-selector RULE to give

$sf = ...(fooInt @Char dOrdChar)...

and now we see that we should have specialised fooInt.


What's the solution? Not trying to specialise foo: it's only a method selector. Rather, we'd need the Specialiser to run that built-in RULE on the fly, just as it does a lot of substitution on the fly, to expose new specialision opportunities.

I don't think this would be hard to achieve, in the code for specExpr, here

specExpr env expr@(App {})
  = go expr []
  where
    go (App fun arg) args = do (arg', uds_arg) <- specExpr env arg
                               (fun', uds_app) <- go fun (arg':args)
                               return (App fun' arg', uds_arg `plusUDs` uds_app)

    go (Var f)       args = case specVar env f of
                                Var f' -> return (Var f', mkCallUDs env f' args)
                                e'     -> return (e', emptyUDs) -- I don't expect this!
    go other         _    = specExpr env other

But it's not trivial either. I'm not sure if it's worth it.

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information