I have worked on this yet, and now I have working fusion without using common paper gadgets.
{-
I deduced most of the specialized types by taking a generalized construct and then introducing type definitions until I hit the bottom. I have retained the general design here for ease of comparison.
data HExpoTree fa = HExpoTree (Maybe a) (f (fa)) | HNoExpoTree type g ~> h = forall a. ga -> ha class HFunctor f where ffmap :: Functor g => (a -> b) -> fga -> fgb hfmap :: (Functor g, Functor h) => (g ~> h) -> (fg ~> fh) instance HFunctor HExpoTree where ffmap f HNoExpoTree = HNoExpoTree ffmap f (HExpoTree xy) = HExpoTree (fmap fx) (fmap (fmap f) y) hfmap f HNoExpoTree = HNoExpoTree hfmap f (HExpoTree xy) = HExpoTree x (f (fmap fy)) type Alg fg = fg ~> g newtype Mu fa = In { unIn :: f (Mu f) a } instance HFunctor f => Functor (Mu f) where fmap f (In r) = In (ffmap fr) hfold :: (HFunctor f, Functor g) => Alg fg -> (Mu f ~> g) hfold m (In u) = m (hfmap (hfold m) u)
An Alg ExpoTreeH g can be decomposed into a product of two natural transformations:
type ExpoTreeAlg g = forall a. Maybe a -> g (ga) -> ga type NoExpoTreeAlg g = forall a. ga {-
The natural transformation c ~> x very interesting and very necessary. Here's the constructed translation:
hbuild :: HFunctor f => (forall x. Alg fx -> (c ~> x)) -> (c ~> Mu f) hbuild g = g In newtype I :: (* -> *) where I :: x -> I x deriving (Show, Eq, Functor, Foldable, Traversable) -- Needs to be a newtype, otherwise RULE firer gets bamboozled newtype ExpoTreeBuilder c = ETP {runETP :: (forall x. Functor x => (forall a. Maybe a -> x (xa) -> xa) -> (forall a. xa) -> (forall a. ca -> xa) )} {-# NOINLINE build #-} build :: ExpoTreeBuilder c -> forall a. ca -> ExpoTree a build g = runETP g ExpoTree NoExpoTree
A new type of builder function is needed because GHC 8.0 does not know how to disable this rule.
Now the label merge rule:
{-# RULES "ExpoTree fold/build" forall (g :: ExpoTreeBuilder c) c (f :: ExpoTreeAlg g) (n :: NoExpoTreeAlg g). fold fn (build gc) = runETP gfnc #-}
Implementation of 'match' with 'build':
{-
Implement "appl" with "fold" (we need to define a custom functor to determine the return type.)
newtype PFunTree a = PFunTree { runPFunTree :: Tree -> Maybe a } deriving (Functor) {-
Putting it all together:
applmatch :: Tree -> Tree -> Maybe () applmatch x = runPFunTree (appl (match x))
We can check the kernel again with -ddump-simpl . Unfortunately, although we have successfully fused the TrieMap data TrieMap , we are left with suboptimal code due to fmap in match . Elimination of this inefficiency is left for future work.