Improving default method optimisation
Consider this:
class C a where
op1, op2 :: [a] -> [a]
op2 xs = op1 (op1 (op1 xs)) ++ reverse (reverse (op1 (op1 xs)))
++ op1 (op1 (op1 xs)) ++ reverse (reverse (op1 (op1 xs)))
++ op1 (op1 (op1 xs)) ++ reverse (reverse (op1 (op1 xs)))
instance C Int where
op1 xs = [sum xs]
instance C b => C [b] where
op1 xs = map op1 xs
The default method behaves as if it was macro-expanded (compulsorily inlined) at every call site, so we get nice non-recursive bindings. When we write
class C a where
op1 :: a -> a
op2 :: a -> a
op2 x = <op2-dm-expr>
instance C T where
op1 = <op1-T-expr>
it's as if we wrote
instance C T where
op1 = <op1-T-expr>
op2 = <op2-dm-expr> -- Macro-expanded here
But this is deeply strange and requires special compulsory-inlining of
default methods. See Note [INLINE and default methods]
in TcInstDcls, and the strange IsDefaultMethod
constructor of TcSpecPrags
.
Moreover it doesn't work if you to it by hand:
class C a where
op1, op2 :: [a] -> [a]
dmop2 xs = op1 (op1 (op1 xs)) ++ reverse (reverse (op1 (op1 xs)))
++ op1 (op1 (op1 xs)) ++ reverse (reverse (op1 (op1 xs)))
++ op1 (op1 (op1 xs)) ++ reverse (reverse (op1 (op1 xs)))
instance C Int where
op1 xs = [sum xs]
op2 = dmop2
instance C b => C [b] where
op1 xs = map op1 xs
op2 = dmop2
We get much less good code. I've made dmop2
artifically big so that
it doesn't inline. If it inlines, it's just like macro-expansion
again, and things are good. But if dmop2
is recursive, that doesn't work either.
What should happen is that the speicaliser should speicalise dmop2
for the instance.
But that doesn't happen.
TL;DR: This ticket is about improving the specialiser so that it does a good job of this example. Then we can get rid of the strange code around default methods.