Better floating and SpecConstr
foo = ...(go @T ($df @T dMonadT))... where dMonadT :: Monad T go :: forall m. Monad m => blah
In HEAD we had worker/wrappered go, so it looked like
foo = ...(go @T returnT bindT)... where returnT :: forall b. b -> T b bindT :: forall b c. T b -> (b -> T c) -> T c
in HEAD, SpecConstr then specialised
go for the function
bindT, which was good. But in my
work on !7847 I am not w/w-ing dictionaries any more. And alas
SpecConstr totally fails to specialise the call to
foo = ...(go @T ($df @T dMonadT))...
What to do? It seems bad that SpecConstr does worse on a dictionary argument than on a higher-order functional argument. The former should really be easier to handle. Can we beef up SpecConstr a bit?
First, SpecConstr would have a better chance if we floated the arg to top level, thus
lvl_dict :: Monad T lvl_dict = $df @T dMonadT foo = ...(go @T lvl_dict))...
Why doesn't this happen right now? Because FloatOut doesn't float out constant expressions (even if they go to the top) if the context is strict. See FloatOut
Note [Floating to the top], esp this text
* Arguments t = f (g True) If f is lazy, we /do/ float (g True) because then we can allocate the thunk statically rather than dynamically. But if f is strict we don't (see the use of idDmdSig in lvlApp). It's not clear if this test is worth the bother: it's only about CAFs!
Second, SpecConstr would need to specialise
lvl_dict. That requires two things:
In the call, SpecConstr must treat
lvl_dictas a value: see
In the body of
go, SpecConstr must treat a use of the dict in a class-op selection a very like a case expression. E.g.
go @m (d :: Monad m) = ....(bind_sel @m d @t1 @t2 e1 e2)...
In the body of
gowe select the bind method with
bind_sel(a ClassOpId). Very like
(case d of MD _ _ bind _ -> bind)