debind :: PTerm -> PTerm -> PTerm
-- For every arg which is an AppBind, lift it out
debind b tm = let (tm', (bs, _)) = runState (db' tm) ([], 0) in
                  bindAll (reverse bs) tm'
  where
    db' :: PTerm -> State ([(Name, FC, PTerm)], Int) PTerm
    db' (PAppBind _ (PApp fc t args) [])
         = db' (PAppBind fc t args)
    db' (PAppBind fc t args)
        = do args' <- dbs args
             (bs, n) <- get
             let nm = sUN ("_bindApp" ++ show n)
             put ((nm, fc, PApp fc t args') : bs, n+1)
             return (PRef fc [] nm)
    db' (PApp fc t args)
         = do t' <- db' t
              args' <- mapM dbArg args
              return (PApp fc t' args')
    db' (PWithApp fc t arg)
         = do t' <- db' t
              arg' <- db' arg
              return (PWithApp fc t' arg')
    db' (PLam fc n nfc ty sc) = return (PLam fc n nfc ty (debind b sc))
    db' (PLet fc rc n nfc ty v sc)
        = do v' <- db' v
             return (PLet fc rc n nfc ty v' (debind b sc))
    db' (PCase fc s opts) = do s' <- db' s
                               return (PCase fc s' (map (pmap (debind b)) opts))
    db' (PPair fc hls p l r) = do l' <- db' l
                                  r' <- db' r
                                  return (PPair fc hls p l' r')
    db' (PDPair fc hls p l t r) = do l' <- db' l
                                     r' <- db' r
                                     return (PDPair fc hls p l' t r')
    db' (PRunElab fc t ns) = fmap (\tm -> PRunElab fc tm ns) (db' t)
    db' (PConstSugar fc tm) = liftM (PConstSugar fc) (db' tm)
    db' t = return t

    dbArg a = do t' <- db' (getTm a)
                 return (a { getTm = t' })

    dbs [] = return []
    dbs (a : as) = do let t = getTm a
                      t' <- db' t
                      as' <- dbs as
                      return (a { getTm = t' } : as')

    bindAll [] tm = tm
    bindAll ((n, fc, t) : bs) tm
       = PApp fc b [pexp t, pexp (PLam fc n NoFC Placeholder (bindAll bs tm))]
debind :: PTerm -> PTerm -> PTerm
debind b tm = let (tm', (bs, _)) = runState (db' tm) ([], 0) in
                  bindAll (reverse bs) tm'
  where
    db' :: PTerm -> State ([(Name, FC, PTerm)], Int) PTerm
    db' (PAppBind _ (PApp fc t args) [])
         = db' (PAppBind fc t args)
    db' (PAppBind fc t args)
        = do args' <- dbs args
             (bs, n) <- get
             let nm = sUN ("_bindApp" ++ show n)
             put ((nm, fc, PApp fc t args') : bs, n+1)
             return (PRef fc [] nm)
    db' (PApp fc t args)
         = do t' <- db' t
              args' <- mapM dbArg args
              return (PApp fc t' args')
    db' (PWithApp fc t arg)
         = do t' <- db' t
              arg' <- db' arg
              return (PWithApp fc t' arg')
    db' (PLam fc n nfc ty sc) = return (PLam fc n nfc ty (debind b sc))
    db' (PLet fc rc n nfc ty v sc)
        = do v' <- db' v
             return (PLet fc rc n nfc ty v' (debind b sc))
    db' (PCase fc s opts) = do s' <- db' s
                               return (PCase fc s' (map (pmap (debind b)) opts))
    db' (PPair fc hls p l r) = do l' <- db' l
                                  r' <- db' r
                                  return (PPair fc hls p l' r')
    db' (PDPair fc hls p l t r) = do l' <- db' l
                                     r' <- db' r
                                     return (PDPair fc hls p l' t r')
    db' (PRunElab fc t ns) = fmap (\tm -> PRunElab fc tm ns) (db' t)
    db' (PConstSugar fc tm) = liftM (PConstSugar fc) (db' tm)
    db' t = return t

    dbArg a = do t' <- db' (getTm a)
                 return (a { getTm = t' })

    dbs [] = return []
    dbs (a : as) = do let t = getTm a
                      t' <- db' t
                      as' <- dbs as
                      return (a { getTm = t' } : as')

    bindAll [] tm = tm
    bindAll ((n, fc, t) : bs) tm
       = PApp fc b [pexp t, pexp (PLam fc n NoFC Placeholder (bindAll bs tm))]
debind b tm = let (tm', (bs, _)) = runState (db' tm) ([], 0) in
                  bindAll (reverse bs) tm'
  where
    db' :: PTerm -> State ([(Name, FC, PTerm)], Int) PTerm
    db' (PAppBind _ (PApp fc t args) [])
         = db' (PAppBind fc t args)
    db' (PAppBind fc t args)
        = do args' <- dbs args
             (bs, n) <- get
             let nm = sUN ("_bindApp" ++ show n)
             put ((nm, fc, PApp fc t args') : bs, n+1)
             return (PRef fc [] nm)
    db' (PApp fc t args)
         = do t' <- db' t
              args' <- mapM dbArg args
              return (PApp fc t' args')
    db' (PWithApp fc t arg)
         = do t' <- db' t
              arg' <- db' arg
              return (PWithApp fc t' arg')
    db' (PLam fc n nfc ty sc) = return (PLam fc n nfc ty (debind b sc))
    db' (PLet fc rc n nfc ty v sc)
        = do v' <- db' v
             return (PLet fc rc n nfc ty v' (debind b sc))
    db' (PCase fc s opts) = do s' <- db' s
                               return (PCase fc s' (map (pmap (debind b)) opts))
    db' (PPair fc hls p l r) = do l' <- db' l
                                  r' <- db' r
                                  return (PPair fc hls p l' r')
    db' (PDPair fc hls p l t r) = do l' <- db' l
                                     r' <- db' r
                                     return (PDPair fc hls p l' t r')
    db' (PRunElab fc t ns) = fmap (\tm -> PRunElab fc tm ns) (db' t)
    db' (PConstSugar fc tm) = liftM (PConstSugar fc) (db' tm)
    db' t = return t

    dbArg a = do t' <- db' (getTm a)
                 return (a { getTm = t' })

    dbs [] = return []
    dbs (a : as) = do let t = getTm a
                      t' <- db' t
                      as' <- dbs as
                      return (a { getTm = t' } : as')

    bindAll [] tm = tm
    bindAll ((n, fc, t) : bs) tm
       = PApp fc b [pexp t, pexp (PLam fc n NoFC Placeholder (bindAll bs tm))]