[GHC] #10417: Rule matching not "seeing through" floating and type lambda (and maybe cast)

GHC ghc-devs at haskell.org
Fri May 15 16:41:06 UTC 2015


#10417: Rule matching not "seeing through" floating and type lambda (and maybe
cast)
-------------------------------------+-------------------------------------
        Reporter:  duncan            |                   Owner:
            Type:  bug               |                  Status:  new
        Priority:  normal            |               Milestone:
       Component:  Compiler          |                 Version:  7.10.1
      Resolution:                    |                Keywords:
Operating System:  Unknown/Multiple  |            Architecture:
 Type of failure:  None/Unknown      |  Unknown/Multiple
      Blocked By:                    |               Test Case:
 Related Tickets:                    |                Blocking:
                                     |  Differential Revisions:
-------------------------------------+-------------------------------------
Description changed by simonpj:

Old description:

> The nub of the problem looks like this. We have a RULE:
>
> {{{#!hs
> {-# RULES "Sequence/Sequence"
>     forall a b c. Sequence (Sequence a b) c
>                 = Sequence a (Sequence b c)
>   #-}
> }}}
> and some core code that looks like:
> {{{#!hs
> failing_example =
>   \ @ s_azo ->
>     Sequence ((failing_example3) `cast` ...) (...)
>
> failing_example3 =
>   \ @ s_azo ->
>     Sequence (...) (...)
> }}}
> We would hope that the rule would match here but clearly it does not.
> There's obviously a few things in the way of a straightforward match:
>
>   1. the key subterm is floated out
>   2. there's a `\ @ ->` type lambda in the way in the subterm
>   3. there's a `cast` in the way in the subterm
>
> It appears that the problem is not the cast but the combination of 1 & 2.
> A reduced example shows the problem occurs with just the first two. That
> said, the real code would also rely on it working with the `cast`.
>
> ----
>
> The rest of this is the code needed to construct the example. The code is
> also attached to the ticket.
>
> {{{#!hs
> {-# LANGUAGE GADTs, RankNTypes, TypeOperators, ScopedTypeVariables,
>              TypeFamilies, KindSignatures, DataKinds,
> UndecidableInstances #-}
>
> module RulesExample (working_example2) where
>
> import Prelude hiding (pure, (<*>))
>
> data Decoder s s' where
>
>   -- explicit encoding of sequencing
>
>   Done     :: Decoder s s
>   Sequence :: Decoder a b -> Decoder b c -> Decoder a c
>
>   -- primitive operation in "chained" style
>
>   ConsumeWord :: Decoder (Word :*: s) s' -> Decoder s s'
>   AlterStack  :: (a -> b) -> Decoder b s' -> Decoder a s'
>
> data a :*: b = a :*: b
> infixr 5 :*:
> }}}
> We compose actions using the explicit encoding of sequencing
> {{{#!hs
> a >>> b = Sequence a b
> }}}
> Aside: in the real thing we go via the category class:
>
>         instance Category Decoder where
>           id    = Done
>           a . b = Sequence b a
>           {-# INLINE (.) #-}
>
>         the Category module defines >>> as flip (.)
>
> Now our primitives are in the chained style, and we turn them into the
> explicit sequencing style by chaining them with Done:
>
> {{{#!hs
> consumeWord :: Decoder s (Word :*: s)
> consumeWord = ConsumeWord Done
>
> alter :: (a -> b) -> Decoder a b
> alter f = AlterStack f Done
> }}}
> example of explicit sequencing:
> {{{
> consumeWord >>> consumeWord >>> ...
> }}}
> but naively this will give us things like:
> {{{
> ConsumeWord Done `Sequence` ConsumeWord Done `Sequence` ...
> }}}
> and we can interpret this much faster if it's in the "chained" style
> {{{
> ConsumeWord (ConsumeWord (... ) )
> }}}
> Aside: there are good reasons not to just try and construct this chained
> style in the first place. We can't do it everywhere, and where it would
> fail we would get bad code. Rather we want to start with something ok and
> optimise it locally.
>
> So, we use RULES to reassociate things and eliminate the Sequence and
> Done where possible:
> {{{#!hs
> {-# RULES "Sequence/Done"
>     forall sa. Sequence Done sa = sa
>   #-}
>
> {-# RULES "Sequence/Sequence"
>     forall a b c. Sequence (Sequence a b) c
>                 = Sequence a (Sequence b c)
>   #-}
>
> {-# RULES "Sequence/ConsumeWord"
>     forall sa sa'. Sequence (ConsumeWord sa) sa'
>                  = ConsumeWord (Sequence sa sa')
>   #-}
>
> {-# RULES "Sequence/AlterStack"
>     forall f sa sa'. Sequence (AlterStack f sa) sa'
>                    = AlterStack f (Sequence sa sa')
>   #-}
> }}}
> Now for starters, ghc produces warnings for all three RULES like:
> {{{
> Rule "Sequence/Done" may never fire
>       because ‘RulesExample.Sequence’ might inline first
>     Probable fix: add an INLINE[n] or NOINLINE[n] pragma on
> ‘RulesExample.Sequence’
> }}}
> but it's currently syntactically invalid to write such a pragma:
> {{{
> {-# INLINE [0] Done #-}
> {-# INLINE [0] Sequence #-}
> }}}
> and also, the warning isn't wholly correct, the rule can fire:
> {{{#!hs
> working_example = consumeWord >>> consumeWord
>               >>> alter (\(a :*: b :*: s) -> (a,b) :*: s)
>
> }}}
> actually, this isn't yet quite enough because we cannot persuade ghc to
> inline consumeWord, because it's just a static composition of
> constructors, not even if we hit it with the INLINE pragma:
> {{{#!hs
> {-# INLINE consumeWord #-}
> }}}
> we have to resort to the bigger hammer of an inline rule:
> {{{#!hs
> {-# RULES "inline consumeWord" consumeWord = ConsumeWord Done #-}
> }}}
> now this is good, we get exactly the core that we want:
> {{{
> $WDone = \ @ s_anC -> Done @~ <s_anC>_N
>
> working_example3 =
>   \ @ b_aw2 ds_dAY ->
>     case ds_dAY of _ { :*: a_aoc ds1_dAZ ->
>     case ds1_dAZ of _ { :*: b1_aod s_aoe -> :*: (a_aoc, b1_aod) s_aoe }
>     }
>
> working_example2 =
>   \ @ b_aw2 -> AlterStack (working_example3) ($WDone)
>
> working_example1 = \ @ b_aw2 -> ConsumeWord (working_example2)
>
> working_example = \ @ b_aw2 -> ConsumeWord (working_example1)
> }}}
> This is perfect. Each constructor is statically allocated and refers to
> the next one in the chain. This allows the interpreter to walk over this
> without triggering any allocation, and the chain style means that in the
> usual case it doesn't have to do control flow call/return work for the
> Sequence/Done.
>
> Note already that we may have an issue with rule matching on Done, since
> it does get changed later into $WDone. So it could actually be useful to
> be able
> to write:
> {{{
> {-# INLINE [0] Done #-}
> }}}
> Ok, so, what doesn't work...
>
> Floating things out is not itself a problem, this still works fine, the
> rules fire. So apparently the `\ @ b_aw2 -> ` type abstraction isn't a
> problem for the rule matching.
> {{{#!hs
> second_example = consumeWord >>> part2
>
> part2 = consumeWord >>> part3
>
> part3 = alter (\(a :*: b :*: s) -> (a,b) :*: s)
> }}}
> However the following fails to work. This example is actually extracted
> from the core of a real example that doesn't work.
> {{{#!hs
> artificial_example = Sequence failing_example3 failing_example1
>   where
>     failing_example3 = Sequence failing_example5 failing_example4
>     failing_example5 = Sequence Done failing_example6
>     failing_example6 = ConsumeWord Done
>     failing_example4 = ConsumeWord Done
>     failing_example1 = AlterStack failing_example2 Done
>     failing_example2 (a :*: b :*: s) = (a,b) :*: s
> }}}
> here's a specific example of a rule not matching:
> {{{
> ailing_example3 =
>   \ @ a_ax3 ->
>     Sequence
>       (failing_example4) (failing_example4)
>
> failing_example4 =
>   \ @ s_awp -> ConsumeWord ($WDone)
> }}}
> This should match the rule
> {{{
>     Sequence (ConsumeWord sa) sa'
>   = ConsumeWord (Sequence sa sa')
> }}}
> but apparently does not. The combo of the floating out and the type
> lambda seem to get in the way. Note that in this example ghc has also
> CSE'd the failing_example4 with failing_example5 and so failing_example4
> is used twice.
>
> But if that's the problem here, there also equivalent instances of the
> Sequence/Sequence rule not matching. Also, ConsumeWord is certainly
> CONLIKE so we'd hope even if ghc had already CSEd that it'd allow the
> duplication for the rule matching. In fact looking at the simpl
> iterations I think the
> rule would have gotten a chance to fire before the CSE occured.
>
> In the real example that the above was extracted from we also have
> `cast`s in the way.
>
> Unfortunately we need more infrastructure to illustrate the full example.
> I don't think the details of this infrastructure is important, except
> that it ends up giving us `cast` in the core.
>
> We're building an abstraction on top of Decoder to let us use Applicative
> style.
> {{{#!hs
> data Fun a = Id
>            | Cons a
>            | O (Fun a) (Fun a)
>
> -- this requires UndecidableInstances
> type family Apply (f :: Fun *) (a :: *) :: *
> type instance Apply Id        a = a
> type instance Apply (Cons  x) a = x :*: a
> type instance Apply (O f g)   a = Apply f (Apply g a)
> }}}
> we pair up stack changes and their inverses
> {{{#!hs
> data DecoderA0 s (f :: Fun *) a
>    = DecoderA0  (Decoder s (Apply f s))  -- change the stack from   s ->
> f s
>                 (Apply f s -> (s, a))    -- change stack back from  f s
> -> s
>

> {-# INLINE pure0 #-}
> pure0 :: forall a s. a -> DecoderA0 s Id a
> pure0 a = DecoderA0 Done (\s -> (s, a))
>
> {-# INLINE app0 #-}
> app0 :: DecoderA0 s f (a -> b) -> DecoderA0 (Apply f s) g a -> DecoderA0
> s (g `O` f) b
> app0 (DecoderA0 d f) (DecoderA0 d' x) =
>     DecoderA0 (d >>> d') appStack
>   where
>     -- just flipped <*> for state monad
>     appStack = \s -> case x s of
>                        (s', x') -> case f s' of
>                                      (s'', f') -> (s'', f' x')
>
> {-# INLINE close0 #-}
> close0 :: forall s f a. DecoderA0 s f a -> Decoder s (a :*: s)
> close0 (DecoderA0 d se) = d >>> alter se'
>   where
>     se' :: Apply f s -> (a :*: s)
>     se' s = case se s of (s', a) -> a :*: s'
> }}}
> now we can abstract over the stack change, because it always takes us
> back to the same initial stack state
> {{{#!hs
> data DecoderA a where
>   DecoderA :: (forall s. DecoderA0 s f a) ->  DecoderA a
> }}}
> in the real thing we use Functor and Applicative instances, but we only
> need bits
> {{{#!hs
> pure a                    = DecoderA (pure0 a)
> DecoderA f <*> DecoderA x = DecoderA (app0 f x)
>
> infixl 4 <*>
> }}}
> now conversions functions:
> {{{#!hs
> {-# INLINE embedDecoderA #-}
> embedDecoderA :: DecoderA a -> Decoder s (a :*: s)
> embedDecoderA (DecoderA ap) = close0 ap
>
> {-# INLINE[1] embedDecoder #-}
> embedDecoder :: forall a. (forall s. Decoder s (a :*: s)) -> DecoderA a
> embedDecoder dec =
>     DecoderA popResultAp
>   where
>     popResultAp :: DecoderA0 s (Cons a) a
>     popResultAp = DecoderA0 dec (\(a :*: s) -> (s, a))
> }}}
> ok, we can finally build our example...
> {{{#!hs
> failing_example =
>     embedDecoderA $
>       pure (,) <*> embedDecoder consumeWord
>                <*> embedDecoder consumeWord
> }}}
> So all the applicative stuff gets inlined away, but we end up with this
> core:
> {{{
> $WDone = \ @ s_anC -> Done @~ <s_anC>_N
>
> failing_example6 = \ @ s_azo -> ConsumeWord ($WDone)
>
> failing_example4 = \ @ s_azo -> ConsumeWord ($WDone)
>
> failing_example5 =
>   \ @ s_azo ->
>     Sequence (($WDone) `cast` ...) ((failing_example6) `cast` ...)
>
> failing_example3 =
>   \ @ s_azo ->
>     Sequence
>       ((failing_example5) `cast` ...) ((failing_example4) `cast` ...)
>
> failing_example2 =
>   \ @ s_azo s1_aox ->
>     case s1_aox `cast` ... of _ { :*: a_aoH s2_aoI ->
>     case s2_aoI `cast` ... of _ { :*: a1_Xpa s3_Xpc ->
>     :*: (a1_Xpa, a_aoH) (s3_Xpc `cast` ...)
>     }
>     }
>
> failing_example1 =
>   \ @ s_azo -> AlterStack (failing_example2) ($WDone)
>
> failing_example =
>   \ @ s_azo ->
>     Sequence ((failing_example3) `cast` ...) (failing_example1)
> }}}
> We have two examples of RULES not mathing where we would have hoped.
>
> Firstly, we have:
> {{{
> Sequence (($WDone) `cast` ...) (...)
> }}}
> We would of course hope that the rule Sequence Done sa = sa would match
> here
>
> It's unclear if this is due to the `$WDone` or the `cast`. Certainly the
> conversion from Done to `$WDone` can be worked around by using a function
> {{{
> {-# INLINE [0] done #-}
> done = Done
> }}}
> and changing the code and rules to use that.
>
> So the main problem is the one mentioned at the top of the ticket:
> {{{
> failing_example =
>   \ @ s_azo ->
>     Sequence ((failing_example3) `cast` ...) (...)
>
> failing_example3 =
>   \ @ s_azo ->
>     Sequence (...) (...)
> }}}
> We would hope that this rule would match
> {{{
>   Sequence (Sequence a b) c = Sequence a (Sequence b c)
> }}}
> So this is the same issue as in the artificial example but now
> additionally there's a `cast` in the way in the subterm.

New description:

 The nub of the problem looks like this. We have a RULE:

 {{{#!hs
 {-# RULES "Sequence/Sequence"
     forall a b c. Sequence (Sequence a b) c
                 = Sequence a (Sequence b c)
   #-}
 }}}
 and some core code that looks like:
 {{{#!hs
 failing_example =
   \ @ s_azo ->
     Sequence ((failing_example3) `cast` ...) (...)

 failing_example3 =
   \ @ s_azo ->
     Sequence (...) (...)
 }}}
 We would hope that the rule would match here but clearly it does not.
 There's obviously a few things in the way of a straightforward match:

   1. the key subterm is floated out
   2. there's a `\ @ ->` type lambda in the way in the subterm
   3. there's a `cast` in the way in the subterm

 It appears that the problem is not the cast but the combination of 1 & 2.
 A reduced example shows the problem occurs with just the first two. That
 said, the real code would also rely on it working with the `cast`.

 ----

 The rest of this is the code needed to construct the example. The code is
 also attached to the ticket.

 {{{#!hs
 {-# LANGUAGE GADTs, RankNTypes, TypeOperators, ScopedTypeVariables,
              TypeFamilies, KindSignatures, DataKinds, UndecidableInstances
 #-}

 module RulesExample (working_example2) where

 import Prelude hiding (pure, (<*>))

 data Decoder s s' where

   -- explicit encoding of sequencing

   Done     :: Decoder s s
   Sequence :: Decoder a b -> Decoder b c -> Decoder a c

   -- primitive operation in "chained" style

   ConsumeWord :: Decoder (Word :*: s) s' -> Decoder s s'
   AlterStack  :: (a -> b) -> Decoder b s' -> Decoder a s'

 data a :*: b = a :*: b
 infixr 5 :*:
 }}}
 We compose actions using the explicit encoding of sequencing
 {{{#!hs
 a >>> b = Sequence a b
 }}}
 Aside: in the real thing we go via the category class:
 {{{
         instance Category Decoder where
           id    = Done
           a . b = Sequence b a
           {-# INLINE (.) #-}
 }}}
 The Category module defines `>>>` as `flip (.)`

 Now our primitives are in the chained style, and we turn them into the
 explicit sequencing style by chaining them with Done:

 {{{#!hs
 consumeWord :: Decoder s (Word :*: s)
 consumeWord = ConsumeWord Done

 alter :: (a -> b) -> Decoder a b
 alter f = AlterStack f Done
 }}}
 example of explicit sequencing:
 {{{
 consumeWord >>> consumeWord >>> ...
 }}}
 but naively this will give us things like:
 {{{
 ConsumeWord Done `Sequence` ConsumeWord Done `Sequence` ...
 }}}
 and we can interpret this much faster if it's in the "chained" style
 {{{
 ConsumeWord (ConsumeWord (... ) )
 }}}
 Aside: there are good reasons not to just try and construct this chained
 style in the first place. We can't do it everywhere, and where it would
 fail we would get bad code. Rather we want to start with something ok and
 optimise it locally.

 So, we use RULES to reassociate things and eliminate the Sequence and Done
 where possible:
 {{{#!hs
 {-# RULES "Sequence/Done"
     forall sa. Sequence Done sa = sa
   #-}

 {-# RULES "Sequence/Sequence"
     forall a b c. Sequence (Sequence a b) c
                 = Sequence a (Sequence b c)
   #-}

 {-# RULES "Sequence/ConsumeWord"
     forall sa sa'. Sequence (ConsumeWord sa) sa'
                  = ConsumeWord (Sequence sa sa')
   #-}

 {-# RULES "Sequence/AlterStack"
     forall f sa sa'. Sequence (AlterStack f sa) sa'
                    = AlterStack f (Sequence sa sa')
   #-}
 }}}
 Now for starters, ghc produces warnings for all three RULES like:
 {{{
 Rule "Sequence/Done" may never fire
       because ‘RulesExample.Sequence’ might inline first
     Probable fix: add an INLINE[n] or NOINLINE[n] pragma on
 ‘RulesExample.Sequence’
 }}}
 but it's currently syntactically invalid to write such a pragma:
 {{{
 {-# INLINE [0] Done #-}
 {-# INLINE [0] Sequence #-}
 }}}
 and also, the warning isn't wholly correct, the rule can fire:
 {{{#!hs
 working_example = consumeWord >>> consumeWord
               >>> alter (\(a :*: b :*: s) -> (a,b) :*: s)

 }}}
 actually, this isn't yet quite enough because we cannot persuade ghc to
 inline consumeWord, because it's just a static composition of
 constructors, not even if we hit it with the INLINE pragma:
 {{{#!hs
 {-# INLINE consumeWord #-}
 }}}
 we have to resort to the bigger hammer of an inline rule:
 {{{#!hs
 {-# RULES "inline consumeWord" consumeWord = ConsumeWord Done #-}
 }}}
 now this is good, we get exactly the core that we want:
 {{{
 $WDone = \ @ s_anC -> Done @~ <s_anC>_N

 working_example3 =
   \ @ b_aw2 ds_dAY ->
     case ds_dAY of _ { :*: a_aoc ds1_dAZ ->
     case ds1_dAZ of _ { :*: b1_aod s_aoe -> :*: (a_aoc, b1_aod) s_aoe }
     }

 working_example2 =
   \ @ b_aw2 -> AlterStack (working_example3) ($WDone)

 working_example1 = \ @ b_aw2 -> ConsumeWord (working_example2)

 working_example = \ @ b_aw2 -> ConsumeWord (working_example1)
 }}}
 This is perfect. Each constructor is statically allocated and refers to
 the next one in the chain. This allows the interpreter to walk over this
 without triggering any allocation, and the chain style means that in the
 usual case it doesn't have to do control flow call/return work for the
 Sequence/Done.

 Note already that we may have an issue with rule matching on Done, since
 it does get changed later into $WDone. So it could actually be useful to
 be able
 to write:
 {{{
 {-# INLINE [0] Done #-}
 }}}
 Ok, so, what doesn't work...

 Floating things out is not itself a problem, this still works fine, the
 rules fire. So apparently the `\ @ b_aw2 -> ` type abstraction isn't a
 problem for the rule matching.
 {{{#!hs
 second_example = consumeWord >>> part2

 part2 = consumeWord >>> part3

 part3 = alter (\(a :*: b :*: s) -> (a,b) :*: s)
 }}}
 However the following fails to work. This example is actually extracted
 from the core of a real example that doesn't work.
 {{{#!hs
 artificial_example = Sequence failing_example3 failing_example1
   where
     failing_example3 = Sequence failing_example5 failing_example4
     failing_example5 = Sequence Done failing_example6
     failing_example6 = ConsumeWord Done
     failing_example4 = ConsumeWord Done
     failing_example1 = AlterStack failing_example2 Done
     failing_example2 (a :*: b :*: s) = (a,b) :*: s
 }}}
 here's a specific example of a rule not matching:
 {{{
 ailing_example3 =
   \ @ a_ax3 ->
     Sequence
       (failing_example4) (failing_example4)

 failing_example4 =
   \ @ s_awp -> ConsumeWord ($WDone)
 }}}
 This should match the rule
 {{{
     Sequence (ConsumeWord sa) sa'
   = ConsumeWord (Sequence sa sa')
 }}}
 but apparently does not. The combo of the floating out and the type lambda
 seem to get in the way. Note that in this example ghc has also CSE'd the
 failing_example4 with failing_example5 and so failing_example4 is used
 twice.

 But if that's the problem here, there also equivalent instances of the
 Sequence/Sequence rule not matching. Also, ConsumeWord is certainly
 CONLIKE so we'd hope even if ghc had already CSEd that it'd allow the
 duplication for the rule matching. In fact looking at the simpl iterations
 I think the
 rule would have gotten a chance to fire before the CSE occured.

 In the real example that the above was extracted from we also have `cast`s
 in the way.

 Unfortunately we need more infrastructure to illustrate the full example.
 I don't think the details of this infrastructure is important, except that
 it ends up giving us `cast` in the core.

 We're building an abstraction on top of Decoder to let us use Applicative
 style.
 {{{#!hs
 data Fun a = Id
            | Cons a
            | O (Fun a) (Fun a)

 -- this requires UndecidableInstances
 type family Apply (f :: Fun *) (a :: *) :: *
 type instance Apply Id        a = a
 type instance Apply (Cons  x) a = x :*: a
 type instance Apply (O f g)   a = Apply f (Apply g a)
 }}}
 we pair up stack changes and their inverses
 {{{#!hs
 data DecoderA0 s (f :: Fun *) a
    = DecoderA0  (Decoder s (Apply f s))  -- change the stack from   s -> f
 s
                 (Apply f s -> (s, a))    -- change stack back from  f s ->
 s


 {-# INLINE pure0 #-}
 pure0 :: forall a s. a -> DecoderA0 s Id a
 pure0 a = DecoderA0 Done (\s -> (s, a))

 {-# INLINE app0 #-}
 app0 :: DecoderA0 s f (a -> b) -> DecoderA0 (Apply f s) g a -> DecoderA0 s
 (g `O` f) b
 app0 (DecoderA0 d f) (DecoderA0 d' x) =
     DecoderA0 (d >>> d') appStack
   where
     -- just flipped <*> for state monad
     appStack = \s -> case x s of
                        (s', x') -> case f s' of
                                      (s'', f') -> (s'', f' x')

 {-# INLINE close0 #-}
 close0 :: forall s f a. DecoderA0 s f a -> Decoder s (a :*: s)
 close0 (DecoderA0 d se) = d >>> alter se'
   where
     se' :: Apply f s -> (a :*: s)
     se' s = case se s of (s', a) -> a :*: s'
 }}}
 now we can abstract over the stack change, because it always takes us back
 to the same initial stack state
 {{{#!hs
 data DecoderA a where
   DecoderA :: (forall s. DecoderA0 s f a) ->  DecoderA a
 }}}
 in the real thing we use Functor and Applicative instances, but we only
 need bits
 {{{#!hs
 pure a                    = DecoderA (pure0 a)
 DecoderA f <*> DecoderA x = DecoderA (app0 f x)

 infixl 4 <*>
 }}}
 now conversions functions:
 {{{#!hs
 {-# INLINE embedDecoderA #-}
 embedDecoderA :: DecoderA a -> Decoder s (a :*: s)
 embedDecoderA (DecoderA ap) = close0 ap

 {-# INLINE[1] embedDecoder #-}
 embedDecoder :: forall a. (forall s. Decoder s (a :*: s)) -> DecoderA a
 embedDecoder dec =
     DecoderA popResultAp
   where
     popResultAp :: DecoderA0 s (Cons a) a
     popResultAp = DecoderA0 dec (\(a :*: s) -> (s, a))
 }}}
 ok, we can finally build our example...
 {{{#!hs
 failing_example =
     embedDecoderA $
       pure (,) <*> embedDecoder consumeWord
                <*> embedDecoder consumeWord
 }}}
 So all the applicative stuff gets inlined away, but we end up with this
 core:
 {{{
 $WDone = \ @ s_anC -> Done @~ <s_anC>_N

 failing_example6 = \ @ s_azo -> ConsumeWord ($WDone)

 failing_example4 = \ @ s_azo -> ConsumeWord ($WDone)

 failing_example5 =
   \ @ s_azo ->
     Sequence (($WDone) `cast` ...) ((failing_example6) `cast` ...)

 failing_example3 =
   \ @ s_azo ->
     Sequence
       ((failing_example5) `cast` ...) ((failing_example4) `cast` ...)

 failing_example2 =
   \ @ s_azo s1_aox ->
     case s1_aox `cast` ... of _ { :*: a_aoH s2_aoI ->
     case s2_aoI `cast` ... of _ { :*: a1_Xpa s3_Xpc ->
     :*: (a1_Xpa, a_aoH) (s3_Xpc `cast` ...)
     }
     }

 failing_example1 =
   \ @ s_azo -> AlterStack (failing_example2) ($WDone)

 failing_example =
   \ @ s_azo ->
     Sequence ((failing_example3) `cast` ...) (failing_example1)
 }}}
 We have two examples of RULES not mathing where we would have hoped.

 Firstly, we have:
 {{{
 Sequence (($WDone) `cast` ...) (...)
 }}}
 We would of course hope that the rule Sequence Done sa = sa would match
 here

 It's unclear if this is due to the `$WDone` or the `cast`. Certainly the
 conversion from Done to `$WDone` can be worked around by using a function
 {{{
 {-# INLINE [0] done #-}
 done = Done
 }}}
 and changing the code and rules to use that.

 So the main problem is the one mentioned at the top of the ticket:
 {{{
 failing_example =
   \ @ s_azo ->
     Sequence ((failing_example3) `cast` ...) (...)

 failing_example3 =
   \ @ s_azo ->
     Sequence (...) (...)
 }}}
 We would hope that this rule would match
 {{{
   Sequence (Sequence a b) c = Sequence a (Sequence b c)
 }}}
 So this is the same issue as in the artificial example but now
 additionally there's a `cast` in the way in the subterm.

--

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


More information about the ghc-tickets mailing list