title: Transitivity of Auto-Specialization in GHC
body: <p>From <a href="http://www.haskell.org/ghc/docs/7.6.3/html/users_guide/pragmas.html#idp49866112" rel="noreferrer">the docs</a> for GHC 7.6:</p>
  
  <blockquote>
    <p>[Y]ou often don't even need the SPECIALIZE pragma in the first place. When compiling a module M, GHC's optimiser (with -O) automatically considers each top-level overloaded function declared in M, and specialises it for the different types at which it is called in M. The optimiser also considers each imported INLINABLE overloaded function, and specialises it for the different types at which it is called in M.</p>
  </blockquote>
  
  <p>and</p>
  
  <blockquote>
    <p>Moreover, given a SPECIALIZE pragma for a function f, GHC will automatically create specialisations for any type-class-overloaded functions called by f, if they are in the same module as the SPECIALIZE pragma, or if they are INLINABLE; and so on, transitively.</p>
  </blockquote>
  
  <p>So GHC should automatically specialize <a href="https://stackoverflow.com/questions/21071706/specialization-with-constraints">some/most/all(?)</a> functions marked <code>INLINABLE</code> <em>without</em> a pragma, and if I use an explicit pragma, the specialization is transitive. My question is: 
  is the <em>auto</em>-specialization transitive?</p>
  
  <p>Specifically, here's a small example:</p>
  
  <p>Main.hs:</p>
  
  <pre><code>import Data.Vector.Unboxed as U
  import Foo
  
  main =
      let y = Bar $ Qux $ U.replicate 11221184 0 :: Foo (Qux Int)
          (Bar (Qux ans)) = iterate (plus y) y !! 100
      in putStr $ show $ foldl1' (*) ans
  </code></pre>
  
  <p>Foo.hs:</p>
  
  <pre><code>module Foo (Qux(..), Foo(..), plus) where
  
  import Data.Vector.Unboxed as U
  
  newtype Qux r = Qux (Vector r)
  -- GHC inlines `plus` if I remove the bangs or the Baz constructor
  data Foo t = Bar !t
             | Baz !t
  
  instance (Num r, Unbox r) =&gt; Num (Qux r) where
      {-# INLINABLE (+) #-}
      (Qux x) + (Qux y) = Qux $ U.zipWith (+) x y
  
  {-# INLINABLE plus #-}
  plus :: (Num t) =&gt; (Foo t) -&gt; (Foo t) -&gt; (Foo t)
  plus (Bar v1) (Bar v2) = Bar $ v1 + v2
  </code></pre>
  
  <p>GHC specializes the call to <code>plus</code>, but does <em>not</em> specialize <code>(+)</code> in the <code>Qux</code> <code>Num</code> instance which kills performance. </p>
  
  <p>However, an explicit pragma </p>
  
  <pre><code>{-# SPECIALIZE plus :: Foo (Qux Int) -&gt; Foo (Qux Int) -&gt; Foo (Qux Int) #-}
  </code></pre>
  
  <p>results in <em>transitive</em> specialization as the docs indicate, so <code>(+)</code> is specialized and the code is 30x faster (both compiled with <code>-O2</code>). Is this expected behavior? Should I only expect <code>(+)</code> to be specialized transitively with an explicit pragma?</p>
  
  <hr>
  
  <p><strong>UPDATE</strong></p>
  
  <p>The docs for 7.8.2 haven't changed, and the behavior is the same, so this question is still relevant.</p>