memo :: (State -> ScpM (FreeVars, Out Term))
     -> State -> ScpM (FreeVars, Out Term)
memo opt state = traceRenderM (">sc", residualiseState state) >>
  do
    (ps, input_fvs) <- fmap (promises &&& inputFvs) get
    case [ (S.fromList (rn_fvs (fvs p)), fun p `varApps` rn_fvs tb_noninput_fvs)
         | p <- ps
         , Just rn_lr <- [match (meaning p) state]
         , let rn_fvs = map (safeRename ("tieback: FVs " ++ pPrintRender (fun p)) rn_lr)  -- NB: If tb contains a dead PureHeap binding (hopefully impossible) then it may have a free variable that I can't rename, so "rename" will cause an error. Not observed in practice yet.
               (tb_input_fvs, tb_noninput_fvs) = partition (`S.member` input_fvs) (fvs p)
          -- Because we don't abstract over top-level free variables (this is necessary for type checking e.g. polymorphic uses of error):
         , all (\x -> rename rn_lr x == x) tb_input_fvs
         ] of
      res:_ -> {- traceRender ("tieback", residualiseState state, fst res) $ -} do
        traceRenderM ("<sc", residualiseState state, res)
        return res
      [] -> {- traceRender ("new drive", residualiseState state) $ -} do
        x <- freshHName
        let vs = stateFreeVars state
            vs_list = S.toList vs
            noninput_vs_list = filter (`S.notMember` input_fvs) vs_list
        traceRenderM ("memo", x, vs_list) `seq` return ()
        
        promise P { fun = x, fvs = vs_list, meaning = state }
        (_fvs', e') <- opt state
        assertRender ("sc: FVs", _fvs', vs) (_fvs' `S.isSubsetOf` vs) $ return ()
        
        traceRenderM ("<sc", residualiseState state, (S.fromList vs_list, e'))
        
        bind x (lambdas noninput_vs_list e')
        return (vs, x `varApps` noninput_vs_list)
memo :: (State -> ScpM (FreeVars, Out Term))
     -> State -> ScpM (FreeVars, Out Term)
memo opt state = traceRenderM (">sc", residualiseState state) >>
  do
    (ps, input_fvs) <- fmap (promises &&& inputFvs) get
    case [ (S.fromList (rn_fvs (fvs p)), fun p `varApps` rn_fvs tb_noninput_fvs)
         | p <- ps
         , Just rn_lr <- [match (meaning p) state]
         , let rn_fvs = map (safeRename ("tieback: FVs " ++ pPrintRender (fun p)) rn_lr)  -- NB: If tb contains a dead PureHeap binding (hopefully impossible) then it may have a free variable that I can't rename, so "rename" will cause an error. Not observed in practice yet.
               (tb_input_fvs, tb_noninput_fvs) = partition (`S.member` input_fvs) (fvs p)
          -- Because we don't abstract over top-level free variables (this is necessary for type checking e.g. polymorphic uses of error):
         , all (\x -> rename rn_lr x == x) tb_input_fvs
         ] of
      res:_ -> {- traceRender ("tieback", residualiseState state, fst res) $ -} do
        traceRenderM ("<sc", residualiseState state, res)
        return res
      [] -> {- traceRender ("new drive", residualiseState state) $ -} do
        x <- freshHName
        let vs = stateFreeVars state
            vs_list = S.toList vs
            noninput_vs_list = filter (`S.notMember` input_fvs) vs_list
        traceRenderM ("memo", x, vs_list) `seq` return ()
        
        promise P { fun = x, fvs = vs_list, meaning = state }
        (_fvs', e') <- opt state
        assertRender ("sc: FVs", _fvs', vs) (_fvs' `S.isSubsetOf` vs) $ return ()
        
        traceRenderM ("<sc", residualiseState state, (S.fromList vs_list, e'))
        
        bind x (lambdas noninput_vs_list e')
        return (vs, x `varApps` noninput_vs_list)
memo opt state = traceRenderM (">sc", residualiseState state) >>
  do
    (ps, input_fvs) <- fmap (promises &&& inputFvs) get
    case [ (S.fromList (rn_fvs (fvs p)), fun p `varApps` rn_fvs tb_noninput_fvs)
         | p <- ps
         , Just rn_lr <- [match (meaning p) state]
         , let rn_fvs = map (safeRename ("tieback: FVs " ++ pPrintRender (fun p)) rn_lr)  -- NB: If tb contains a dead PureHeap binding (hopefully impossible) then it may have a free variable that I can't rename, so "rename" will cause an error. Not observed in practice yet.
               (tb_input_fvs, tb_noninput_fvs) = partition (`S.member` input_fvs) (fvs p)
          -- Because we don't abstract over top-level free variables (this is necessary for type checking e.g. polymorphic uses of error):
         , all (\x -> rename rn_lr x == x) tb_input_fvs
         ] of
      res:_ -> {- traceRender ("tieback", residualiseState state, fst res) $ -} do
        traceRenderM ("<sc", residualiseState state, res)
        return res
      [] -> {- traceRender ("new drive", residualiseState state) $ -} do
        x <- freshHName
        let vs = stateFreeVars state
            vs_list = S.toList vs
            noninput_vs_list = filter (`S.notMember` input_fvs) vs_list
        traceRenderM ("memo", x, vs_list) `seq` return ()
        
        promise P { fun = x, fvs = vs_list, meaning = state }
        (_fvs', e') <- opt state
        assertRender ("sc: FVs", _fvs', vs) (_fvs' `S.isSubsetOf` vs) $ return ()
        
        traceRenderM ("<sc", residualiseState state, (S.fromList vs_list, e'))
        
        bind x (lambdas noninput_vs_list e')
        return (vs, x `varApps` noninput_vs_list)