Skip to content

Fold/build fails to fire when using applicative operators

See also

This program does not compile optimally:

import Data.Foldable

foo :: Int -> IO ()
foo x = print x
{-# OPAQUE foo #-}

main = for_ (liftA2 (,) [0 .. 10] [0 .. 10]) (\(x,y) -> foo (x + y))

I expected all the lists to fuse away, but looking at the core shows that some lists remain:

Rec {
go3_a2fy
  = \ x_a2fz ->
      : (I# x_a2fz)
        (case x_a2fz of wild_X1E {
           __DEFAULT -> go3_a2fy (+# wild_X1E 1#);
           10# -> []
         })
end Rec }

ys_a1yw = go3_a2fy 0#

Rec {
$wgo3_s2gs
  = \ x_s2gk eta_s2gl ->
      join {
        exit_X2 eta_X1
          = case x_s2gk of wild_X1E {
              __DEFAULT -> $wgo3_s2gs (+# wild_X1E 1#) eta_X1;
              10# -> eta_X1
            } } in
      joinrec {
        go1_s2fO ds_a2fI eta_X1
          = case ds_a2fI of {
              [] -> jump exit_X2 eta_X1;
              : y_a2fL ys_a2fM ->
                case ((foo (case y_a2fL of { I# y_a2g9 -> I# (+# x_s2gk y_a2g9) }))
                      `cast` <Co:2> :: ...)
                       eta_X1
                of
                { (# ipv_a1ha, ipv1_a1hb #) ->
                jump go1_s2fO ys_a2fM ipv_a1ha
                }
            }; } in
      jump go1_s2fO ys_a1yw eta_s2gl
end Rec }

main_s2gb
  = \ eta_s2gl ->
      case $wgo3_s2gs 0# eta_s2gl of ww_s2go { __DEFAULT ->
      (# ww_s2go, () #)
      }

It seems that ys_a1yw is floated out too soon, which separates the foldr from the build and prevents fusion. After the first simplifier pass, the core looks like this:

ys_a1yw
  = build (\ @b_a1yK c_a1yL n_a1yM -> eftIntFB c_a1yL n_a1yM 0# 10#)

main
  = eftIntFB
      (\ ds_a1yA ds1_a1yB ->
         foldr
           ((\ ds2_a1yC ds3_a1yD s_a1h8 ->
               case ((foo ($fNumInt_$c+ ds_a1yA ds2_a1yC)) `cast` <Co:2> :: ...)
                      s_a1h8
               of
               { (# ipv_a1ha, ipv1_a1hb #) ->
               (ds3_a1yD `cast` <Co:2> :: ...) ipv_a1ha
               })
            `cast` <Co:10> :: ...)
           ds1_a1yB
           ys_a1yw)
      ((\ s_a1yg -> (# s_a1yg, () #)) `cast` <Co:3> :: ...)
      0#
      10#

The fusion does happen if I replace liftA2 by its definition:

main = for_ [(,) x y | x <- [0 .. 10], y <- [0 .. 10]] (\(x,y) -> foo (x + y))

Environment

  • GHC version used: 9.6.4 and 9.8.2
Edited by Simon Peyton Jones
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information