[GHC] #16357: Add `oneShot` to the implementation of foldlM

GHC ghc-devs at haskell.org
Sat Feb 23 03:34:17 UTC 2019


#16357: Add `oneShot` to the implementation of foldlM
-------------------------------------+-------------------------------------
           Reporter:  autotaker      |             Owner:  (none)
               Type:  bug            |            Status:  new
           Priority:  normal         |         Milestone:
          Component:                 |           Version:  8.9
  libraries/base                     |
           Keywords:                 |  Operating System:  Unknown/Multiple
       Architecture:                 |   Type of failure:  None/Unknown
  Unknown/Multiple                   |
          Test Case:                 |        Blocked By:
           Blocking:                 |   Related Tickets:
Differential Rev(s):                 |         Wiki Page:
-------------------------------------+-------------------------------------
 The current (473632d7671619ee08a2a0025aa22bd4f79eca2d) implementation of
 `Data.Foldable.foldlM` is the like this
 {{{#!hs
 foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
 foldlM f z0 xs = foldr c return xs z0
   -- See Note [List fusion and continuations in 'c']
   where c x k z = f z x >>= k
         {-# INLINE c #-}
 }}}

 It generates an inefficient core for the following example.
 {{{#!hs
 f :: Int -> IO Int
 f = foldlM (\a b -> pure $! (a + b)) 0 (filter even [1..n])
 }}}

 Generated core:
 {{{#!hs
 -- RHS size: {terms: 48, types: 22, coercions: 12, joins: 0/1}
 Main.$wf [InlPrag=NOUSERINLINE[2]]
   :: GHC.Prim.Int#
      -> GHC.Prim.State# GHC.Prim.RealWorld
      -> (# GHC.Prim.State# GHC.Prim.RealWorld, Int #)
 [GblId,
  Arity=2,
  Caf=NoCafRefs,
  Str=<L,U><L,U>,
  Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
          WorkFree=True, Expandable=True, Guidance=IF_ARGS [0 0] 216 30}]
 Main.$wf
   = \ (ww_s6TZ :: GHC.Prim.Int#)
       (w_s6TW :: GHC.Prim.State# GHC.Prim.RealWorld) ->
       case GHC.Prim.># 1# ww_s6TZ of {
         __DEFAULT ->
           letrec {
             go_a5un [Occ=LoopBreaker] :: GHC.Prim.Int# -> Int -> IO Int
             [LclId, Arity=1, Str=<L,U>, Unf=OtherCon []]
             go_a5un
               = \ (x_a5uo :: GHC.Prim.Int#) ->
                   case GHC.Prim.remInt# x_a5uo 2# of {
                     __DEFAULT ->
                       case GHC.Prim.==# x_a5uo ww_s6TZ of {
                         __DEFAULT -> go_a5un (GHC.Prim.+# x_a5uo 1#);
                         1# ->
                           (GHC.Base.$fApplicativeIO4 @ Int)
                           `cast` (<Int>_R ->_R Sym (GHC.Types.N:IO[0]
 <Int>_R)
                                   :: (Int
                                       -> GHC.Prim.State#
 GHC.Prim.RealWorld
                                       -> (# GHC.Prim.State#
 GHC.Prim.RealWorld, Int #))
                                      ~R# (Int -> IO Int))
                       };
                     0# ->
                       Main.main_c
                         @ Int
                         (GHC.Types.I# x_a5uo)
                         (case GHC.Prim.==# x_a5uo ww_s6TZ of {
                            __DEFAULT -> go_a5un (GHC.Prim.+# x_a5uo 1#);
                            1# ->
                              (GHC.Base.$fApplicativeIO4 @ Int)
                              `cast` (<Int>_R ->_R Sym (GHC.Types.N:IO[0]
 <Int>_R)
                                      :: (Int
                                          -> GHC.Prim.State#
 GHC.Prim.RealWorld
                                          -> (# GHC.Prim.State#
 GHC.Prim.RealWorld, Int #))
                                         ~R# (Int -> IO Int))
                          })
                   }; } in
           ((go_a5un 1# Main.main4)
            `cast` (GHC.Types.N:IO[0] <Int>_R
                    :: IO Int
                       ~R# (GHC.Prim.State# GHC.Prim.RealWorld
                            -> (# GHC.Prim.State# GHC.Prim.RealWorld, Int
 #))))
             w_s6TW;
         1# -> (# w_s6TW, Main.main4 #)
       }
 }}}
 It seems that the main loop `go_a5un` is not eta-expanded.

 I think problem is that `oneShot` is missing in the definition of
 `foldlM`.

 When I changed the definition of `foldlM` as follows,
 {{{#!hs
 import GHC.Exts(oneShot)
 foldlM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
 foldlM f z0 xs = foldr c return xs z0
   -- See Note [List fusion and continuations in 'c']
   where c x k = oneShot (\z -> f z x >>= k)
         {-# INLINE c #-}
 }}}

 Then, the main loop of the `wf` is eta-expaned as expected.
 {{{#!hs
 -- RHS size: {terms: 64, types: 46, coercions: 0, joins: 1/1}
 Main.$wf [InlPrag=NOUSERINLINE[2]]
   :: GHC.Prim.Int#
      -> GHC.Prim.State# GHC.Prim.RealWorld
      -> (# GHC.Prim.State# GHC.Prim.RealWorld, Int #)
 [GblId,
  Arity=2,
  Caf=NoCafRefs,
  Str=<L,U><L,U>,
  Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
          WorkFree=True, Expandable=True, Guidance=IF_ARGS [0 0] 136 30}]
 Main.$wf
   = \ (ww_s6Xc :: GHC.Prim.Int#)
       (w_s6X9 :: GHC.Prim.State# GHC.Prim.RealWorld) ->
       case GHC.Prim.># 1# ww_s6Xc of {
         __DEFAULT ->
           joinrec {
             go_s6WG [Occ=LoopBreaker]
               :: GHC.Prim.Int#
                  -> Int
                  -> GHC.Prim.State# GHC.Prim.RealWorld
                  -> (# GHC.Prim.State# GHC.Prim.RealWorld, Int #)
             [LclId[JoinId(3)],
              Arity=3,
              Str=<L,U><L,U(U)><L,U>,
              Unf=OtherCon []]
             go_s6WG (x_a5xy :: GHC.Prim.Int#)
                     (eta_B2 :: Int)
                     (eta1_Xz :: GHC.Prim.State# GHC.Prim.RealWorld)
               = case GHC.Prim.remInt# x_a5xy 2# of {
                   __DEFAULT ->
                     case GHC.Prim.==# x_a5xy ww_s6Xc of {
                       __DEFAULT -> jump go_s6WG (GHC.Prim.+# x_a5xy 1#)
 eta_B2 eta1_Xz;
                       1# -> (# eta1_Xz, eta_B2 #)
                     };
                   0# ->
                     case eta_B2 of { GHC.Types.I# x1_a5t8 ->
                     case GHC.Prim.==# x_a5xy ww_s6Xc of {
                       __DEFAULT ->
                         jump go_s6WG
                           (GHC.Prim.+# x_a5xy 1#)
                           (GHC.Types.I# (GHC.Prim.+# x1_a5t8 x_a5xy))
                           eta1_Xz;
                       1# -> (# eta1_Xz, GHC.Types.I# (GHC.Prim.+# x1_a5t8
 x_a5xy) #)
                     }
                     }
                 }; } in
           jump go_s6WG 1# Main.main4 w_s6X9;
         1# -> (# w_s6X9, Main.main4 #)
       }
 }}}

-- 
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/16357>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list