- | Like 'reify' but with common sub-expression elimination and variable hoisting
reifySmart :: forall dom p pVar a
    .  ( AlphaEq dom dom (FODomain dom p pVar) [(VarId,VarId)]
       , Syntactic a
       , Domain a ~ HODomain dom p pVar
       , p :< Typeable
       , Project Let (FODomain dom p pVar)
       )
    => Int  -- Max number of iterations
    -> (forall c. ASTF (FODomain dom p pVar) c -> Bool)
    -> MkInjDict (FODomain dom p pVar)
    -> MkSubEnv (FODomain dom p pVar)
    -> a
    -> ASTF (FODomain dom p pVar) (Internal a)
reifySmart limit hoistOver mkId mkSub =
    flip evalState 0 . (codeMotion3 limit hoistOver prjDictFO mkId mkSub <=< reifyM . desugar)

reifySmart :: forall dom p pVar a
    .  ( AlphaEq dom dom (FODomain dom p pVar) [(VarId,VarId)]
       , Syntactic a
       , Domain a ~ HODomain dom p pVar
       , p :< Typeable
       , Project Let (FODomain dom p pVar)
       )
    => Int  -- Max number of iterations
    -> (forall c. ASTF (FODomain dom p pVar) c -> Bool)
    -> MkInjDict (FODomain dom p pVar)
    -> MkSubEnv (FODomain dom p pVar)
    -> a
    -> ASTF (FODomain dom p pVar) (Internal a)
reifySmart limit hoistOver mkId mkSub =
    flip evalState 0 . (codeMotion3 limit hoistOver prjDictFO mkId mkSub <=< reifyM . desugar)
reifySmart limit hoistOver mkId mkSub =
    flip evalState 0 . (codeMotion3 limit hoistOver prjDictFO mkId mkSub <=< reifyM . desugar)