vars :: Fold AST (Set Var -> (Set Var, Set Var))
vars = go
  where
    go :: Fold AST (Set Var -> (Set Var, Set Var))
    go VarA v                   = useVar v
    go ExpA (LetE v _ _ e1 e2)  = go ExpA e1 `mappend`
                                  bindVar v (go ExpA e2)
    go ExpA (LamE vtaus e)      = bindVars (map fst vtaus) (go ExpA e)
    go ExpA (BindE v _ p1 p2)   = go ExpA p1 `mappend`
                                  bindVar v (go ExpA p2)
    go ExpA (ForE _ loopvs p) = foldMap (go ExpA) es `mappend`
                                  bindVars vs (go ExpA p)
      where
        (vs, es) = unzip loopvs
    go w    a                   = foldFam go w a

    useVar :: Var -> (Set Var -> (Set Var, Set Var))
    useVar v = \bound -> (if v `Set.member` bound then mempty else Set.singleton v, mempty)

    bindVar :: Var
            -> (Set Var -> (Set Var, Set Var))
            -> (Set Var -> (Set Var, Set Var))
    bindVar v m = \bound -> m (Set.insert v bound)

    bindVars :: [Var]
             -> (Set Var -> (Set Var, Set Var))
             -> (Set Var -> (Set Var, Set Var))
    bindVars vs m = \bound -> m (bound `Set.union` Set.fromList vs)
vars :: Fold AST (Set Var -> (Set Var, Set Var))
vars = go
  where
    go :: Fold AST (Set Var -> (Set Var, Set Var))
    go VarA v                   = useVar v
    go ExpA (LetE v _ _ e1 e2)  = go ExpA e1 `mappend`
                                  bindVar v (go ExpA e2)
    go ExpA (LamE vtaus e)      = bindVars (map fst vtaus) (go ExpA e)
    go ExpA (BindE v _ p1 p2)   = go ExpA p1 `mappend`
                                  bindVar v (go ExpA p2)
    go ExpA (ForE _ loopvs p) = foldMap (go ExpA) es `mappend`
                                  bindVars vs (go ExpA p)
      where
        (vs, es) = unzip loopvs
    go w    a                   = foldFam go w a

    useVar :: Var -> (Set Var -> (Set Var, Set Var))
    useVar v = \bound -> (if v `Set.member` bound then mempty else Set.singleton v, mempty)

    bindVar :: Var
            -> (Set Var -> (Set Var, Set Var))
            -> (Set Var -> (Set Var, Set Var))
    bindVar v m = \bound -> m (Set.insert v bound)

    bindVars :: [Var]
             -> (Set Var -> (Set Var, Set Var))
             -> (Set Var -> (Set Var, Set Var))
    bindVars vs m = \bound -> m (bound `Set.union` Set.fromList vs)
vars = go
  where
    go :: Fold AST (Set Var -> (Set Var, Set Var))
    go VarA v                   = useVar v
    go ExpA (LetE v _ _ e1 e2)  = go ExpA e1 `mappend`
                                  bindVar v (go ExpA e2)
    go ExpA (LamE vtaus e)      = bindVars (map fst vtaus) (go ExpA e)
    go ExpA (BindE v _ p1 p2)   = go ExpA p1 `mappend`
                                  bindVar v (go ExpA p2)
    go ExpA (ForE _ loopvs p) = foldMap (go ExpA) es `mappend`
                                  bindVars vs (go ExpA p)
      where
        (vs, es) = unzip loopvs
    go w    a                   = foldFam go w a

    useVar :: Var -> (Set Var -> (Set Var, Set Var))
    useVar v = \bound -> (if v `Set.member` bound then mempty else Set.singleton v, mempty)

    bindVar :: Var
            -> (Set Var -> (Set Var, Set Var))
            -> (Set Var -> (Set Var, Set Var))
    bindVar v m = \bound -> m (Set.insert v bound)

    bindVars :: [Var]
             -> (Set Var -> (Set Var, Set Var))
             -> (Set Var -> (Set Var, Set Var))
    bindVars vs m = \bound -> m (bound `Set.union` Set.fromList vs)