Here is a minimal example reproducing the real problem I'm working on:
One library module:
module Lib where class H h where hash :: (S s)=> s -> h -> s class S s where mix :: s -> Int -> s instance (H x, H y)=> H (x,y) where hash s = \(x,y) -> s `hash` x `hash` y -- make this look "big": `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y `hash` x `hash` y instance H Int where hash s = \n -> s `mix` n
Another, possibly user defined:
module S where import Lib newtype Foo = Foo Int deriving Show instance S Foo where mix (Foo x) y = Foo (x+y)
And our Main
:
module Main where import Lib import S import Criterion.Main main = defaultMain [ bench "foo" $ whnf (hash (Foo 1)) (2::Int,3::Int) ]
Compiling with ghc 8.0.1 with ghc --make -Wall -O2 -rtsopts -ddump-to-file -ddump-simpl -dsuppress-module-prefixes -dsuppress-uniques -ddump-core-stats -ddump-inlinings -fforce-recomp Main.hs
In this test, above 4 μs . If, however, we put INLINE
pragmas on two hash
declarations in Lib
, we will see the required specializations and get a 66 ns runtime.
But I really do not want to embed everything (in the real user Main
she can call hash
the same type many times), I just need a function specialized for each combination of H
and S
in the user code.
Changing INLINE
pragmas to INLINABLE
caused a regression of the old behavior (as I believe, since the GHC inlining heuristic is still in the game). Then I tried to add
{-
for modules Main
and S
, but this generates
Ignoring useless SPECIALISE pragma for class method selector 'hash'
... warnings and the same bad code.
Some limitations:
- It would be acceptable, although not ideal, to require that every instance declaration of
S
include a finite number of pragmas (possibly related to H
) - similar for
H
- it is not acceptable to require users to do
SPECIALIZE
for each combination of S
and H
Is it possible to do this without INLINE?
This is probably the same as the Restricted Specialization and the associated truck ticket https://ghc.haskell.org/trac/ghc/ticket/8668 , but I thought I would ask again and maybe post it as simpler example for GHC Trac.
EDIT : went ahead and opened a ghc ticket: https://ghc.haskell.org/trac/ghc/ticket/13376